diff --git a/IPA.sln b/IPA.sln
new file mode 100644
index 0000000..edd3f93
--- /dev/null
+++ b/IPA.sln
@@ -0,0 +1,45 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25123.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IPA", "IPA\IPA.csproj", "{14092533-98BB-40A4-9AFC-27BB75672A70}"
+ ProjectSection(ProjectDependencies) = postProject
+ {D1390268-F68B-4A55-B50D-EAD25756C8EF} = {D1390268-F68B-4A55-B50D-EAD25756C8EF}
+ {D1C61AF5-0D2D-4752-8203-1C6929025F7C} = {D1C61AF5-0D2D-4752-8203-1C6929025F7C}
+ {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71} = {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71}
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Launcher", "Launcher\Launcher.csproj", "{D1390268-F68B-4A55-B50D-EAD25756C8EF}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IllusionPlugin", "IllusionPlugin\IllusionPlugin.csproj", "{E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IllusionInjector", "IllusionInjector\IllusionInjector.csproj", "{D1C61AF5-0D2D-4752-8203-1C6929025F7C}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {14092533-98BB-40A4-9AFC-27BB75672A70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {14092533-98BB-40A4-9AFC-27BB75672A70}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {14092533-98BB-40A4-9AFC-27BB75672A70}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {14092533-98BB-40A4-9AFC-27BB75672A70}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D1390268-F68B-4A55-B50D-EAD25756C8EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D1390268-F68B-4A55-B50D-EAD25756C8EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D1390268-F68B-4A55-B50D-EAD25756C8EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D1390268-F68B-4A55-B50D-EAD25756C8EF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D1C61AF5-0D2D-4752-8203-1C6929025F7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D1C61AF5-0D2D-4752-8203-1C6929025F7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D1C61AF5-0D2D-4752-8203-1C6929025F7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D1C61AF5-0D2D-4752-8203-1C6929025F7C}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/IPA/IPA.csproj b/IPA/IPA.csproj
new file mode 100644
index 0000000..79b5d81
--- /dev/null
+++ b/IPA/IPA.csproj
@@ -0,0 +1,66 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {14092533-98BB-40A4-9AFC-27BB75672A70}
+ Exe
+ Properties
+ IPA
+ IPA
+ v3.5
+ 512
+ Client
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ none
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IPA/Program.cs b/IPA/Program.cs
new file mode 100644
index 0000000..9fd5fb6
--- /dev/null
+++ b/IPA/Program.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+
+namespace IPA
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ if(args.Length < 1 || !args[0].EndsWith(".exe"))
+ {
+ Fail("Drag an (executable) file on the exe!");
+ }
+
+ string launcherSrc = Path.Combine("IPA", "Launcher.exe");
+ string managedFolder = Path.Combine("IPA", "Managed");
+
+ // Sanitizing
+ if (!File.Exists(launcherSrc)) Fail("Couldn't find launcher! Make sure you extracted all contents of the release archive.");
+ if (!File.Exists(launcherSrc)) Fail("Couldn't find DLLs! Make sure you extracted all contents of the release archive.");
+
+ // Copying
+ try
+ {
+ string projectName = Path.GetFileNameWithoutExtension(args[0]);
+ string launcherPath = Path.Combine(Environment.CurrentDirectory, projectName + "_Patched.exe");
+ string dataPath = Path.Combine(Path.Combine(Environment.CurrentDirectory, projectName + "_Data"), "Managed");
+
+ File.Copy(launcherSrc, launcherPath, true);
+ CopyAll(new DirectoryInfo(managedFolder), new DirectoryInfo(dataPath));
+
+ Console.WriteLine("Successfully copied files!");
+ } catch(Exception e)
+ {
+ Fail("Oops! This should not have happened.\n\n" + e);
+ }
+ }
+
+ public static void CopyAll(DirectoryInfo source, DirectoryInfo target)
+ {
+ Directory.CreateDirectory(target.FullName);
+
+ // Copy each file into the new directory.
+ foreach (FileInfo fi in source.GetFiles())
+ {
+ Console.WriteLine(@"Copying {0}\{1}", target.FullName, fi.Name);
+ fi.CopyTo(Path.Combine(target.FullName, fi.Name), true);
+ }
+
+ // Copy each subdirectory using recursion.
+ foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
+ {
+ DirectoryInfo nextTargetSubDir =
+ target.CreateSubdirectory(diSourceSubDir.Name);
+ CopyAll(diSourceSubDir, nextTargetSubDir);
+ }
+ }
+
+
+ static void Fail(string message)
+ {
+ Console.BackgroundColor = ConsoleColor.Red;
+ Console.ForegroundColor = ConsoleColor.White;
+ Console.Write("{0,80}", "");
+ Console.Write("{0,-80}", " "+message);
+ Console.Write("{0,80}", "");
+
+ Console.ReadLine();
+ Environment.Exit(1);
+ }
+ }
+}
diff --git a/IPA/Properties/AssemblyInfo.cs b/IPA/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..7c8a7df
--- /dev/null
+++ b/IPA/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("IPA")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("IPA")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("14092533-98bb-40a4-9afc-27bb75672a70")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// 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("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/IllusionInjector/Bootstrapper.cs b/IllusionInjector/Bootstrapper.cs
new file mode 100644
index 0000000..43219e9
--- /dev/null
+++ b/IllusionInjector/Bootstrapper.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+
+namespace IllusionInjector
+{
+ class Bootstrapper : MonoBehaviour
+ {
+ public event Action Destroyed = delegate {};
+
+ void OnDestroy()
+ {
+ Destroyed();
+ }
+ }
+}
diff --git a/IllusionInjector/CompositePlugin.cs b/IllusionInjector/CompositePlugin.cs
new file mode 100644
index 0000000..3a4b0ea
--- /dev/null
+++ b/IllusionInjector/CompositePlugin.cs
@@ -0,0 +1,109 @@
+using IllusionPlugin;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using UnityEngine;
+
+namespace IllusionInjector
+{
+ public class CompositePlugin : IPlugin
+ {
+ IEnumerable plugins;
+
+ private delegate void CompositeCall(IPlugin plugin);
+
+ public CompositePlugin(IEnumerable plugins)
+ {
+ this.plugins = plugins;
+ }
+
+ public void OnApplicationStart()
+ {
+ Invoke(plugin => plugin.OnApplicationStart());
+ }
+
+ public void OnApplicationQuit()
+ {
+ Invoke(plugin => plugin.OnApplicationQuit());
+ }
+
+ public void OnLevelWasLoaded(int level)
+ {
+ foreach (var plugin in plugins)
+ {
+ try
+ {
+ plugin.OnLevelWasLoaded(level);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("{0}: {1}", plugin.Name, ex);
+ }
+ }
+ }
+
+
+ private void Invoke(CompositeCall callback)
+ {
+ foreach (var plugin in plugins)
+ {
+ try
+ {
+ callback(plugin);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("{0}: {1}", plugin.Name, ex);
+ }
+ }
+ }
+
+
+
+ public void OnLevelWasInitialized(int level)
+ {
+ foreach (var plugin in plugins)
+ {
+ try
+ {
+ plugin.OnLevelWasInitialized(level);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("{0}: {1}", plugin.Name, ex);
+ }
+ }
+ }
+
+
+ public void OnUpdate()
+ {
+ Invoke(plugin => plugin.OnUpdate());
+ }
+
+ public void OnFixedUpdate()
+ {
+ Invoke(plugin => plugin.OnFixedUpdate());
+ }
+
+
+ public string Name
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public string Version
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public void OnLateUpdate()
+ {
+ Invoke(plugin =>
+ {
+ if (plugin is IEnhancedPlugin)
+ ((IEnhancedPlugin)plugin).OnLateUpdate();
+ });
+ }
+ }
+}
diff --git a/IllusionInjector/ConsoleWindow.cs b/IllusionInjector/ConsoleWindow.cs
new file mode 100644
index 0000000..928795e
--- /dev/null
+++ b/IllusionInjector/ConsoleWindow.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections;
+using System.Runtime.InteropServices;
+using System.IO;
+using System.Text;
+
+namespace Windows
+{
+
+ class GuiConsole
+ {
+ public static void CreateConsole()
+ {
+ if (hasConsole)
+ return;
+ if (oldOut == IntPtr.Zero)
+ oldOut = GetStdHandle( -11 );
+ if (! AllocConsole())
+ throw new Exception("AllocConsole() failed");
+ conOut = CreateFile( "CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero );
+ if (! SetStdHandle(-11, conOut))
+ throw new Exception("SetStdHandle() failed");
+ StreamToConsole();
+ hasConsole = true;
+ }
+ public static void ReleaseConsole()
+ {
+ if (! hasConsole)
+ return;
+ if (! CloseHandle(conOut))
+ throw new Exception("CloseHandle() failed");
+ conOut = IntPtr.Zero;
+ if (! FreeConsole())
+ throw new Exception("FreeConsole() failed");
+ if (! SetStdHandle(-11, oldOut))
+ throw new Exception("SetStdHandle() failed");
+ StreamToConsole();
+ hasConsole = false;
+ }
+ private static void StreamToConsole()
+ {
+ Stream cstm = Console.OpenStandardOutput();
+ StreamWriter cstw = new StreamWriter( cstm, Encoding.Default );
+ cstw.AutoFlush = true;
+ Console.SetOut( cstw );
+ Console.SetError( cstw );
+ }
+ private static bool hasConsole = false;
+ private static IntPtr conOut;
+ private static IntPtr oldOut;
+ [DllImport("kernel32.dll", SetLastError=true)]
+ private static extern bool AllocConsole();
+ [DllImport("kernel32.dll", SetLastError=false)]
+ private static extern bool FreeConsole();
+ [DllImport("kernel32.dll", SetLastError=true)]
+ private static extern IntPtr GetStdHandle( int nStdHandle );
+ [DllImport("kernel32.dll", SetLastError=true)]
+ private static extern bool SetStdHandle(int nStdHandle, IntPtr hConsoleOutput);
+ [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
+ private static extern IntPtr CreateFile(
+ string fileName,
+ int desiredAccess,
+ int shareMode,
+ IntPtr securityAttributes,
+ int creationDisposition,
+ int flagsAndAttributes,
+ IntPtr templateFile );
+ [DllImport("kernel32.dll", ExactSpelling=true, SetLastError=true)]
+ private static extern bool CloseHandle(IntPtr handle);
+ }
+}
\ No newline at end of file
diff --git a/IllusionInjector/IllusionInjector.csproj b/IllusionInjector/IllusionInjector.csproj
new file mode 100644
index 0000000..0d8c799
--- /dev/null
+++ b/IllusionInjector/IllusionInjector.csproj
@@ -0,0 +1,66 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {D1C61AF5-0D2D-4752-8203-1C6929025F7C}
+ Library
+ Properties
+ IllusionInjector
+ IllusionInjector
+ v3.5
+ 512
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ none
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+ ..\Libs\UnityEngine.dll
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {e2848bfb-5432-42f4-8ae0-d2ec0cdf2f71}
+ IllusionPlugin
+
+
+
+
+
\ No newline at end of file
diff --git a/IllusionInjector/Injector.cs b/IllusionInjector/Injector.cs
new file mode 100644
index 0000000..8bc2a95
--- /dev/null
+++ b/IllusionInjector/Injector.cs
@@ -0,0 +1,26 @@
+using System;
+using UnityEngine;
+
+namespace IllusionInjector
+{
+ public static class Injector
+ {
+ private static bool injected = false;
+ public static void Inject()
+ {
+ if (!injected)
+ {
+ injected = true;
+
+ var bootstrapper = new GameObject("Bootstrapper").AddComponent();
+ bootstrapper.Destroyed += Bootstrapper_Destroyed;
+ }
+ }
+
+ private static void Bootstrapper_Destroyed()
+ {
+ var singleton = new GameObject("PluginManager");
+ singleton.AddComponent(); ;
+ }
+ }
+}
diff --git a/IllusionInjector/PluginComponent.cs b/IllusionInjector/PluginComponent.cs
new file mode 100644
index 0000000..e895345
--- /dev/null
+++ b/IllusionInjector/PluginComponent.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using UnityEngine;
+
+namespace IllusionInjector
+{
+ public class PluginComponent : MonoBehaviour
+ {
+ private CompositePlugin plugins;
+ private bool freshlyLoaded = false;
+
+ void Awake()
+ {
+ DontDestroyOnLoad(gameObject);
+
+ if (Environment.CommandLine.Contains("--verbose") && !Screen.fullScreen)
+ {
+ Windows.GuiConsole.CreateConsole();
+ }
+
+ plugins = new CompositePlugin(PluginManager.Plugins);
+ plugins.OnApplicationStart();
+ }
+
+ void Start()
+ {
+ OnLevelWasLoaded(Application.loadedLevel);
+ }
+
+ void Update()
+ {
+ if (freshlyLoaded)
+ {
+ freshlyLoaded = false;
+ plugins.OnLevelWasInitialized(Application.loadedLevel);
+ }
+ plugins.OnUpdate();
+ }
+
+ void LateUpdate()
+ {
+ plugins.OnLateUpdate();
+ }
+
+ void FixedUpdate()
+ {
+ plugins.OnFixedUpdate();
+ }
+
+ void OnDestroy()
+ {
+ plugins.OnApplicationQuit();
+ }
+
+ void OnLevelWasLoaded(int level)
+ {
+ plugins.OnLevelWasLoaded(level);
+ freshlyLoaded = true;
+ }
+
+ }
+}
diff --git a/IllusionInjector/PluginManager.cs b/IllusionInjector/PluginManager.cs
new file mode 100644
index 0000000..2d089d5
--- /dev/null
+++ b/IllusionInjector/PluginManager.cs
@@ -0,0 +1,126 @@
+using IllusionPlugin;
+using System;
+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;
+
+namespace IllusionInjector
+{
+ public static class PluginManager
+ {
+ private static List _Plugins = null;
+
+ ///
+ /// Gets the list of loaded plugins and loads them if necessary.
+ ///
+ public static IEnumerable Plugins
+ {
+ get
+ {
+ if(_Plugins == null)
+ {
+ LoadPlugins();
+ }
+ return _Plugins;
+ }
+ }
+
+
+ private static void LoadPlugins()
+ {
+ string pluginDirectory = Path.Combine(Environment.CurrentDirectory, "Plugins");
+
+ // Process.GetCurrentProcess().MainModule crashes the game and Assembly.GetEntryAssembly() is NULL,
+ // so we need to resort to P/Invoke
+ string exeName = Path.GetFileNameWithoutExtension(AppInfo.StartupPath);
+ Console.WriteLine(exeName);
+ _Plugins = new List();
+
+ if (!Directory.Exists(pluginDirectory)) return;
+
+ String[] files = Directory.GetFiles(pluginDirectory, "*.dll");
+ foreach (var s in files)
+ {
+ _Plugins.AddRange(LoadPluginsFromFile(Path.Combine(pluginDirectory, s), exeName));
+ }
+
+
+ // DEBUG
+ Console.WriteLine("-----------------------------");
+ Console.WriteLine("Loading plugins from {0} and found {1}", pluginDirectory, _Plugins.Count);
+ Console.WriteLine("-----------------------------");
+ foreach (var plugin in _Plugins)
+ {
+
+ Console.WriteLine(" {0}: {1}", plugin.Name, plugin.Version);
+ }
+ Console.WriteLine("-----------------------------");
+
+ }
+
+ private static IEnumerable LoadPluginsFromFile(string file, string exeName)
+ {
+ List plugins = new List();
+
+ if (!File.Exists(file) || !file.EndsWith(".dll", true, null))
+ return plugins;
+
+ try
+ {
+ Assembly assembly = Assembly.LoadFrom(file);
+
+ foreach (Type t in assembly.GetTypes())
+ {
+ if (t.GetInterface("IPlugin") != null)
+ {
+ try
+ {
+
+ IPlugin pluginInstance = Activator.CreateInstance(t) as IPlugin;
+ string[] filter = null;
+
+ if (pluginInstance is IEnhancedPlugin)
+ {
+ filter = ((IEnhancedPlugin)pluginInstance).Filter;
+ }
+
+ if(filter == null || Enumerable.Contains(filter, exeName, StringComparer.OrdinalIgnoreCase))
+ plugins.Add(pluginInstance);
+ }
+ catch (Exception)
+ {
+ }
+ }
+ }
+
+ }
+ catch (Exception)
+ {
+ }
+
+ return plugins;
+ }
+
+ public class AppInfo
+ {
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false)]
+ private static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length);
+ private static HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero);
+ public static string StartupPath
+ {
+ get
+ {
+ StringBuilder stringBuilder = new StringBuilder(260);
+ GetModuleFileName(NullHandleRef, stringBuilder, stringBuilder.Capacity);
+ return stringBuilder.ToString();
+ }
+ }
+ }
+
+ }
+}
diff --git a/IllusionInjector/Properties/AssemblyInfo.cs b/IllusionInjector/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..f1e97dc
--- /dev/null
+++ b/IllusionInjector/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("IllusionInjector")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("IllusionInjector")]
+[assembly: AssemblyCopyright("Copyright © 2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("400a540a-d21f-4609-966b-206059b6e73b")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// 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("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/IllusionPlugin/IEnhancedPlugin.cs b/IllusionPlugin/IEnhancedPlugin.cs
new file mode 100644
index 0000000..304b4ba
--- /dev/null
+++ b/IllusionPlugin/IEnhancedPlugin.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace IllusionPlugin
+{
+ public interface IEnhancedPlugin : IPlugin
+ {
+ ///
+ /// Gets a list of executables this plugin should be excuted on (without the file ending)
+ ///
+ /// { "PlayClub", "PlayClubStudio" }
+ string[] Filter { get; }
+
+ void OnLateUpdate();
+ }
+}
diff --git a/IllusionPlugin/IPlugin.cs b/IllusionPlugin/IPlugin.cs
new file mode 100644
index 0000000..1c4effd
--- /dev/null
+++ b/IllusionPlugin/IPlugin.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace IllusionPlugin
+{
+ ///
+ /// Interface for generic Illusion unity plugins. Every class that implements this will be loaded if the DLL is placed at
+ /// data/Managed/Plugins.
+ ///
+ public interface IPlugin
+ {
+
+ ///
+ /// Gets the name of the plugin.
+ ///
+ string Name { get; }
+
+ ///
+ /// Gets the version of the plugin.
+ ///
+ string Version { get; }
+
+ ///
+ /// Gets invoked when the application is started.
+ ///
+ void OnApplicationStart();
+
+ ///
+ /// Gets invoked when the application is closed.
+ ///
+ void OnApplicationQuit();
+
+ ///
+ /// Gets invoked whenever a level is loaded.
+ ///
+ ///
+ void OnLevelWasLoaded(int level);
+
+ ///
+ /// Gets invoked after the first update cycle after a level was loaded.
+ ///
+ ///
+ void OnLevelWasInitialized(int level);
+
+ ///
+ /// Gets invoked on every graphic update.
+ ///
+ void OnUpdate();
+
+
+ ///
+ /// Gets invoked on ever physics update.
+ ///
+ void OnFixedUpdate();
+ }
+}
diff --git a/IllusionPlugin/IllusionPlugin.XML b/IllusionPlugin/IllusionPlugin.XML
new file mode 100644
index 0000000..11af6a6
--- /dev/null
+++ b/IllusionPlugin/IllusionPlugin.XML
@@ -0,0 +1,178 @@
+
+
+
+ IllusionPlugin
+
+
+
+
+ Gets a list of executables this plugin should be excuted on (without the file ending)
+
+ { "PlayClub", "PlayClubStudio" }
+
+
+
+ Create a New INI file to store or load data
+
+
+
+
+ INIFile Constructor.
+
+
+
+
+
+ Write Data to the INI File
+
+
+ Section name
+
+ Key Name
+
+ Value Name
+
+
+
+ Read Data Value From the Ini File
+
+
+
+
+
+
+
+
+ Interface for generic Illusion unity plugins. Every class that implements this will be loaded if the DLL is placed at
+ data/Managed/Plugins.
+
+
+
+
+ Gets the name of the plugin.
+
+
+
+
+ Gets the version of the plugin.
+
+
+
+
+ Gets invoked when the application is started.
+
+
+
+
+ Gets invoked when the application is closed.
+
+
+
+
+ Gets invoked whenever a level is loaded.
+
+
+
+
+
+ Gets invoked after the first update cycle after a level was loaded.
+
+
+
+
+
+ Gets invoked on every graphic update.
+
+
+
+
+ Gets invoked on ever physics update.
+
+
+
+
+ Allows to get and set preferences for your mod.
+
+
+
+
+ Gets a string from the ini.
+
+ Section of the key.
+ Name of the key.
+ Value that should be used when no value is found.
+ Whether or not the default value should be written if no value is found.
+
+
+
+
+ Gets an int from the ini.
+
+ Section of the key.
+ Name of the key.
+ Value that should be used when no value is found.
+ Whether or not the default value should be written if no value is found.
+
+
+
+
+ Gets a float from the ini.
+
+ Section of the key.
+ Name of the key.
+ Value that should be used when no value is found.
+ Whether or not the default value should be written if no value is found.
+
+
+
+
+ Gets a bool from the ini.
+
+ Section of the key.
+ Name of the key.
+ Value that should be used when no value is found.
+ Whether or not the default value should be written if no value is found.
+
+
+
+
+ Checks whether or not a key exists in the ini.
+
+ Section of the key.
+ Name of the key.
+
+
+
+
+ Sets a float in the ini.
+
+ Section of the key.
+ Name of the key.
+ Value that should be written.
+
+
+
+ Sets an int in the ini.
+
+ Section of the key.
+ Name of the key.
+ Value that should be written.
+
+
+
+ Sets a string in the ini.
+
+ Section of the key.
+ Name of the key.
+ Value that should be written.
+
+
+
+ Sets a bool in the ini.
+
+ Section of the key.
+ Name of the key.
+ Value that should be written.
+
+
+
diff --git a/IllusionPlugin/IllusionPlugin.csproj b/IllusionPlugin/IllusionPlugin.csproj
new file mode 100644
index 0000000..0e1e4e6
--- /dev/null
+++ b/IllusionPlugin/IllusionPlugin.csproj
@@ -0,0 +1,55 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {E2848BFB-5432-42F4-8AE0-D2EC0CDF2F71}
+ Library
+ Properties
+ IllusionPlugin
+ IllusionPlugin
+ v3.5
+ 512
+
+
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ none
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ bin\Release\IllusionPlugin.XML
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/IllusionPlugin/IniFile.cs b/IllusionPlugin/IniFile.cs
new file mode 100644
index 0000000..14e9b81
--- /dev/null
+++ b/IllusionPlugin/IniFile.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace IllusionPlugin
+{
+ ///
+ /// Create a New INI file to store or load data
+ ///
+ internal class IniFile
+ {
+ [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileStringW",
+ SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int GetPrivateProfileString(
+ string lpSection,
+ string lpKey,
+ string lpDefault,
+ StringBuilder lpReturnString,
+ int nSize,
+ string lpFileName);
+
+ [DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileStringW",
+ SetLastError = true,
+ CharSet = CharSet.Unicode, ExactSpelling = true,
+ CallingConvention = CallingConvention.StdCall)]
+ private static extern int WritePrivateProfileString(
+ string lpSection,
+ string lpKey,
+ string lpValue,
+ string lpFileName);
+
+ private string _path = "";
+ public string Path
+ {
+ get
+ {
+ return _path;
+ }
+ set
+ {
+ if (!File.Exists(value))
+ File.WriteAllText(value, "", Encoding.Unicode);
+ _path = value;
+ }
+ }
+
+ ///
+ /// INIFile Constructor.
+ ///
+ ///
+ public IniFile(string INIPath)
+ {
+ this.Path = INIPath;
+ }
+
+ ///
+ /// Write Data to the INI File
+ ///
+ ///
+ /// Section name
+ ///
+ /// Key Name
+ ///
+ /// Value Name
+ public void IniWriteValue(string Section, string Key, string Value)
+ {
+ WritePrivateProfileString(Section, Key, Value, this.Path);
+ }
+
+ ///
+ /// Read Data Value From the Ini File
+ ///
+ ///
+ ///
+ ///
+ ///
+ public string IniReadValue(string Section, string Key)
+ {
+ const int MAX_CHARS = 1023;
+ StringBuilder result = new StringBuilder(MAX_CHARS);
+ GetPrivateProfileString(Section, Key, "", result, MAX_CHARS, this.Path);
+ return result.ToString();
+ }
+ }
+}
diff --git a/IllusionPlugin/ModPrefs.cs b/IllusionPlugin/ModPrefs.cs
new file mode 100644
index 0000000..acdbd7c
--- /dev/null
+++ b/IllusionPlugin/ModPrefs.cs
@@ -0,0 +1,166 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace IllusionPlugin
+{
+ ///
+ /// Allows to get and set preferences for your mod.
+ ///
+ public static class ModPrefs
+ {
+ private static IniFile _instance;
+ private static IniFile Instance
+ {
+ get
+ {
+ if (_instance == null)
+ {
+ _instance = new IniFile(Path.Combine(Environment.CurrentDirectory, "UserData/modprefs.ini"));
+ }
+ return _instance;
+ }
+ }
+
+
+ ///
+ /// Gets a string from the ini.
+ ///
+ /// Section of the key.
+ /// Name of the key.
+ /// Value that should be used when no value is found.
+ /// Whether or not the default value should be written if no value is found.
+ ///
+ public static string GetString(string section, string name, string defaultValue = "", bool autoSave = false)
+ {
+ string value = Instance.IniReadValue(section, name);
+ if (value != "")
+ return value;
+ else if (autoSave)
+ SetString(section, name, defaultValue);
+
+ return defaultValue;
+ }
+
+ ///
+ /// Gets an int from the ini.
+ ///
+ /// Section of the key.
+ /// Name of the key.
+ /// Value that should be used when no value is found.
+ /// Whether or not the default value should be written if no value is found.
+ ///
+ public static int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false)
+ {
+ int value;
+ if (int.TryParse(Instance.IniReadValue(section, name), out value))
+ return value;
+ else if (autoSave)
+ SetInt(section, name, defaultValue);
+
+ return defaultValue;
+ }
+
+
+ ///
+ /// Gets a float from the ini.
+ ///
+ /// Section of the key.
+ /// Name of the key.
+ /// Value that should be used when no value is found.
+ /// Whether or not the default value should be written if no value is found.
+ ///
+ public static float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false)
+ {
+ float value;
+ if (float.TryParse(Instance.IniReadValue(section, name), out value))
+ return value;
+ else if (autoSave)
+ SetFloat(section, name, defaultValue);
+
+ return defaultValue;
+ }
+
+ ///
+ /// Gets a bool from the ini.
+ ///
+ /// Section of the key.
+ /// Name of the key.
+ /// Value that should be used when no value is found.
+ /// Whether or not the default value should be written if no value is found.
+ ///
+ public static bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false)
+ {
+ string sVal = GetString(section, name, null);
+ if (sVal == "1" || sVal == "0")
+ {
+ return sVal == "1";
+ } else if (autoSave)
+ {
+ SetBool(section, name, defaultValue);
+ }
+
+ return defaultValue;
+ }
+
+
+ ///
+ /// Checks whether or not a key exists in the ini.
+ ///
+ /// Section of the key.
+ /// Name of the key.
+ ///
+ public static bool HasKey(string section, string name)
+ {
+ return Instance.IniReadValue(section, name) != null;
+ }
+
+ ///
+ /// Sets a float in the ini.
+ ///
+ /// Section of the key.
+ /// Name of the key.
+ /// Value that should be written.
+ public static void SetFloat(string section, string name, float value)
+ {
+ Instance.IniWriteValue(section, name, value.ToString());
+ }
+
+ ///
+ /// Sets an int in the ini.
+ ///
+ /// Section of the key.
+ /// Name of the key.
+ /// Value that should be written.
+ public static void SetInt(string section, string name, int value)
+ {
+ Instance.IniWriteValue(section, name, value.ToString());
+
+ }
+
+ ///
+ /// Sets a string in the ini.
+ ///
+ /// Section of the key.
+ /// Name of the key.
+ /// Value that should be written.
+ public static void SetString(string section, string name, string value)
+ {
+ Instance.IniWriteValue(section, name, value);
+
+ }
+
+ ///
+ /// Sets a bool in the ini.
+ ///
+ /// Section of the key.
+ /// Name of the key.
+ /// Value that should be written.
+ public static void SetBool(string section, string name, bool value)
+ {
+ Instance.IniWriteValue(section, name, value ? "1" : "0");
+
+ }
+ }
+}
diff --git a/IllusionPlugin/Properties/AssemblyInfo.cs b/IllusionPlugin/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..c2425b9
--- /dev/null
+++ b/IllusionPlugin/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("IllusionPlugin")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("IllusionPlugin")]
+[assembly: AssemblyCopyright("Copyright © 2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("e8cea89d-6c2f-4729-94b3-f355f7db19e5")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// 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("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
\ No newline at end of file
diff --git a/Launcher/Launcher.csproj b/Launcher/Launcher.csproj
new file mode 100644
index 0000000..2293d57
--- /dev/null
+++ b/Launcher/Launcher.csproj
@@ -0,0 +1,104 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {D1390268-F68B-4A55-B50D-EAD25756C8EF}
+ WinExe
+ Properties
+ QuietLauncher
+ Launcher
+ v3.5
+ 512
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\Mono.Cecil.0.9.6.4\lib\net35\Mono.Cecil.dll
+ True
+
+
+ ..\packages\Mono.Cecil.0.9.6.4\lib\net35\Mono.Cecil.Mdb.dll
+ False
+
+
+ ..\packages\Mono.Cecil.0.9.6.4\lib\net35\Mono.Cecil.Pdb.dll
+ False
+
+
+ ..\packages\Mono.Cecil.0.9.6.4\lib\net35\Mono.Cecil.Rocks.dll
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Launcher/Program.cs b/Launcher/Program.cs
new file mode 100644
index 0000000..dcdaeb7
--- /dev/null
+++ b/Launcher/Program.cs
@@ -0,0 +1,299 @@
+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" };
+
+ ///
+ /// The main entry point for the application.
+ ///
+ [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);
+ }
+ }
+
+
+ ///
+ /// The forbidden deed of the gods -- make ALL methods virtual and public
+ ///
+ ///
+ 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);
+ }
+
+ }
+}
diff --git a/Launcher/Properties/AssemblyInfo.cs b/Launcher/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..c2da46c
--- /dev/null
+++ b/Launcher/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("QuietLauncher")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("QuietLauncher")]
+[assembly: AssemblyCopyright("Copyright © 2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("d590f676-c2c0-4b80-ae93-6edf274320e6")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// 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("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Launcher/Properties/Resources.Designer.cs b/Launcher/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..41e16b2
--- /dev/null
+++ b/Launcher/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.34209
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace QuietLauncher.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("QuietLauncher.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/Launcher/Properties/Resources.resx b/Launcher/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/Launcher/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Launcher/Properties/Settings.Designer.cs b/Launcher/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..62bab1b
--- /dev/null
+++ b/Launcher/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.34209
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace QuietLauncher.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/Launcher/Properties/Settings.settings b/Launcher/Properties/Settings.settings
new file mode 100644
index 0000000..3964565
--- /dev/null
+++ b/Launcher/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Launcher/Resources.Designer.cs b/Launcher/Resources.Designer.cs
new file mode 100644
index 0000000..04b9966
--- /dev/null
+++ b/Launcher/Resources.Designer.cs
@@ -0,0 +1,73 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.34209
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace QuietLauncher {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("QuietLauncher.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Byte[].
+ ///
+ internal static byte[] DirectToRift {
+ get {
+ object obj = ResourceManager.GetObject("DirectToRift", resourceCulture);
+ return ((byte[])(obj));
+ }
+ }
+ }
+}
diff --git a/Launcher/Resources.resx b/Launcher/Resources.resx
new file mode 100644
index 0000000..6cd9173
--- /dev/null
+++ b/Launcher/Resources.resx
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+
+ Resources\DirectToRift.exe;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Launcher/Resources/DirectToRift.exe b/Launcher/Resources/DirectToRift.exe
new file mode 100644
index 0000000..bf8cb51
Binary files /dev/null and b/Launcher/Resources/DirectToRift.exe differ
diff --git a/Launcher/packages.config b/Launcher/packages.config
new file mode 100644
index 0000000..1fcd5ba
--- /dev/null
+++ b/Launcher/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Libs/UnityEngine.dll b/Libs/UnityEngine.dll
new file mode 100644
index 0000000..6a4c68c
Binary files /dev/null and b/Libs/UnityEngine.dll differ