@@ -32,7 +32,26 @@ | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<ApplicationIcon>favicon.ico</ApplicationIcon> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net35\Mono.Cecil.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="Mono.Cecil.Mdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net35\Mono.Cecil.Mdb.dll</HintPath> | |||
<Private>False</Private> | |||
</Reference> | |||
<Reference Include="Mono.Cecil.Pdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net35\Mono.Cecil.Pdb.dll</HintPath> | |||
<Private>False</Private> | |||
</Reference> | |||
<Reference Include="Mono.Cecil.Rocks, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net35\Mono.Cecil.Rocks.dll</HintPath> | |||
<Private>False</Private> | |||
</Reference> | |||
<Reference Include="System" /> | |||
<Reference Include="System.Core" /> | |||
<Reference Include="System.Xml.Linq" /> | |||
@@ -41,9 +60,18 @@ | |||
<Reference Include="System.Xml" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="Patcher\BackupManager.cs" /> | |||
<Compile Include="Patcher\Patcher.cs" /> | |||
<Compile Include="Patcher\Virtualizer.cs" /> | |||
<Compile Include="Program.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="packages.config" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Content Include="favicon.ico" /> | |||
</ItemGroup> | |||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
Other similar extension points exist, see Microsoft.Common.targets. | |||
@@ -55,12 +83,10 @@ | |||
<Target Name="AfterBuild"> | |||
<Message Text="Packing..." Importance="normal" /> | |||
<ItemGroup> | |||
<LauncherDlls Include="$(SolutionDir)Launcher\$(OutDir)*.dll" /> | |||
<Launcher Include="$(SolutionDir)Launcher\$(OutDir)Launcher.exe" /> | |||
<Dlls Include="$(SolutionDir)IllusionInjector\$(OutDir)**\*" /> | |||
</ItemGroup> | |||
<Copy SourceFiles="@(Dlls)" DestinationFolder="$(OutputPath)IPA\Managed" /> | |||
<Copy SourceFiles="@(Launcher)" DestinationFolder="$(OutputPath)IPA" /> | |||
<Copy SourceFiles="@(LauncherDlls)" DestinationFolder="$(OutputPath)" /> | |||
</Target> | |||
</Project> |
@@ -0,0 +1,33 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
namespace IPA.Patcher | |||
{ | |||
public class BackupManager | |||
{ | |||
public static void MakeBackup(string file) | |||
{ | |||
File.Copy(file, GetBackupName(file)); | |||
} | |||
private static string GetBackupName(string file) | |||
{ | |||
string backup = file + ".Original"; | |||
if (File.Exists(backup)) | |||
{ | |||
int i = 1; | |||
string backupBase = backup; | |||
while (File.Exists(backup)) | |||
{ | |||
backup = backupBase + i++; | |||
} | |||
} | |||
return backup; | |||
} | |||
} | |||
} |
@@ -0,0 +1,85 @@ | |||
using Mono.Cecil; | |||
using Mono.Cecil.Cil; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
namespace IPA.Patcher | |||
{ | |||
class PatchedModule | |||
{ | |||
private const string ENTRY_TYPE = "Display"; | |||
private FileInfo _File; | |||
private ModuleDefinition _Module; | |||
public static PatchedModule Load(string engineFile) | |||
{ | |||
return new PatchedModule(engineFile); | |||
} | |||
private PatchedModule(string engineFile) | |||
{ | |||
_File = new FileInfo(engineFile); | |||
LoadModules(); | |||
} | |||
private void LoadModules() | |||
{ | |||
var resolver = new DefaultAssemblyResolver(); | |||
resolver.AddSearchDirectory(_File.DirectoryName); | |||
var parameters = new ReaderParameters | |||
{ | |||
AssemblyResolver = resolver, | |||
}; | |||
_Module = ModuleDefinition.ReadModule(_File.FullName, parameters); | |||
} | |||
public bool IsPatched | |||
{ | |||
get | |||
{ | |||
foreach (var @ref in _Module.AssemblyReferences) | |||
{ | |||
if (@ref.Name == "IllusionInjector") return true; | |||
} | |||
return false; | |||
} | |||
} | |||
public void Patch() | |||
{ | |||
// First, let's add the reference | |||
var nameReference = new AssemblyNameReference("IllusionInjector", new Version(1, 0, 0, 0)); | |||
var injectorPath = Path.Combine(_File.DirectoryName, "IllusionInjector.dll"); | |||
_Module.AssemblyReferences.Add(nameReference); | |||
var targetType = FindEntryType(); | |||
if (targetType == null) throw new Exception("Couldn't find entry class. Aborting."); | |||
var targetMethod = targetType.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic); | |||
if (targetMethod == null) | |||
{ | |||
throw new Exception("Couldn't find entry method. Aborting."); | |||
} | |||
var injector = ModuleDefinition.ReadModule(injectorPath); | |||
var methodReference = _Module.Import(injector.GetType("IllusionInjector.Injector").Methods.First(m => m.Name == "Inject")); | |||
targetMethod.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, methodReference)); | |||
_Module.Write(_File.FullName); | |||
} | |||
private TypeDefinition FindEntryType() | |||
{ | |||
return _Module.GetTypes().FirstOrDefault(m => m.Name == ENTRY_TYPE); | |||
} | |||
} | |||
} |
@@ -0,0 +1,104 @@ | |||
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; | |||
public static VirtualizedModule Load(string engineFile) | |||
{ | |||
return new VirtualizedModule(engineFile); | |||
} | |||
private VirtualizedModule(string assemblyFile) | |||
{ | |||
_File = new FileInfo(assemblyFile); | |||
LoadModules(); | |||
} | |||
private void LoadModules() | |||
{ | |||
var resolver = new DefaultAssemblyResolver(); | |||
resolver.AddSearchDirectory(_File.DirectoryName); | |||
var parameters = new ReaderParameters | |||
{ | |||
AssemblyResolver = resolver, | |||
}; | |||
_Module = ModuleDefinition.ReadModule(_File.FullName, parameters); | |||
} | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
/// <param name="module"></param> | |||
public void Virtualize() | |||
{ | |||
foreach (var type in _Module.Types) | |||
{ | |||
VirtualizeType(type); | |||
} | |||
} | |||
private 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; | |||
// Take care of sub types | |||
foreach (var subType in type.NestedTypes) | |||
{ | |||
VirtualizeType(subType); | |||
} | |||
foreach (var method in type.Methods) | |||
{ | |||
if (method.IsManaged | |||
&& 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; | |||
} | |||
} | |||
public bool IsVirtualized | |||
{ | |||
get | |||
{ | |||
return _Module.GetTypes().SelectMany(t => t.Methods.Where(m => m.Name == "Awake")).All(m => m.IsVirtual); | |||
} | |||
} | |||
} | |||
} |
@@ -1,4 +1,5 @@ | |||
using System; | |||
using IPA.Patcher; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
@@ -17,22 +18,46 @@ namespace IPA | |||
string launcherSrc = Path.Combine("IPA", "Launcher.exe"); | |||
string managedFolder = Path.Combine("IPA", "Managed"); | |||
string pluginsFolder = "Plugins"; | |||
string projectName = Path.GetFileNameWithoutExtension(args[0]); | |||
string dataPath = Path.Combine(Path.Combine(Environment.CurrentDirectory, projectName + "_Data"), "Managed"); | |||
string engineFile = Path.Combine(dataPath, "UnityEngine.dll"); | |||
string assemblyFile = Path.Combine(dataPath, "Assembly-Csharp.dll"); | |||
// 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."); | |||
if(!Directory.Exists(dataPath) || !File.Exists(engineFile) || !File.Exists(assemblyFile)) | |||
{ | |||
Fail("Game does not seem to be a Unity project. Could not find the libraries to patch."); | |||
} | |||
// 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); | |||
// Copying | |||
CopyAll(new DirectoryInfo(managedFolder), new DirectoryInfo(dataPath)); | |||
Console.WriteLine("Successfully copied files!"); | |||
if(!Directory.Exists(pluginsFolder)) | |||
{ | |||
Directory.CreateDirectory(pluginsFolder); | |||
} | |||
// Patching | |||
var patchedModule = PatchedModule.Load(engineFile); | |||
if(!patchedModule.IsPatched) | |||
{ | |||
BackupManager.MakeBackup(engineFile); | |||
patchedModule.Patch(); | |||
} | |||
// Virtualizing | |||
var virtualizedModule = VirtualizedModule.Load(assemblyFile); | |||
if(!virtualizedModule.IsVirtualized) | |||
{ | |||
BackupManager.MakeBackup(assemblyFile); | |||
virtualizedModule.Virtualize(); | |||
} | |||
} catch(Exception e) | |||
{ | |||
Fail("Oops! This should not have happened.\n\n" + e); | |||
@@ -46,8 +71,12 @@ namespace IPA | |||
// 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); | |||
string targetFile = Path.Combine(target.FullName, fi.Name); | |||
if (!File.Exists(targetFile) || File.GetLastWriteTimeUtc(targetFile) < fi.LastWriteTimeUtc) | |||
{ | |||
Console.WriteLine(@"Copying {0}\{1}", target.FullName, fi.Name); | |||
fi.CopyTo(Path.Combine(target.FullName, fi.Name), true); | |||
} | |||
} | |||
// Copy each subdirectory using recursion. | |||
@@ -62,13 +91,11 @@ namespace IPA | |||
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(); | |||
Console.Error.Write("ERROR: " + message); | |||
if (!Environment.CommandLine.Contains("--nowait")) | |||
{ | |||
Console.In.ReadToEnd(); | |||
} | |||
Environment.Exit(1); | |||
} | |||
} | |||
@@ -5,11 +5,11 @@ 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: AssemblyTitle("Illusion Plugin Architecture")] | |||
[assembly: AssemblyDescription("")] | |||
[assembly: AssemblyConfiguration("")] | |||
[assembly: AssemblyCompany("")] | |||
[assembly: AssemblyProduct("IPA")] | |||
[assembly: AssemblyProduct("Illusion Plugin Architecture")] | |||
[assembly: AssemblyCopyright("Copyright © 2016")] | |||
[assembly: AssemblyTrademark("")] | |||
[assembly: AssemblyCulture("")] | |||
@@ -32,5 +32,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("1.0.0.0")] | |||
[assembly: AssemblyFileVersion("1.0.0.0")] | |||
[assembly: AssemblyVersion("3.0.0.0")] | |||
[assembly: AssemblyFileVersion("3.0.0.0")] |
@@ -0,0 +1,4 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<packages> | |||
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net35-client" /> | |||
</packages> |
@@ -7,7 +7,7 @@ | |||
<ProjectGuid>{D1390268-F68B-4A55-B50D-EAD25756C8EF}</ProjectGuid> | |||
<OutputType>WinExe</OutputType> | |||
<AppDesignerFolder>Properties</AppDesignerFolder> | |||
<RootNamespace>QuietLauncher</RootNamespace> | |||
<RootNamespace>Launcher</RootNamespace> | |||
<AssemblyName>Launcher</AssemblyName> | |||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> | |||
<FileAlignment>512</FileAlignment> | |||
@@ -31,23 +31,10 @@ | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<ApplicationIcon>syringe.ico</ApplicationIcon> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net35\Mono.Cecil.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="Mono.Cecil.Mdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net35\Mono.Cecil.Mdb.dll</HintPath> | |||
<Private>False</Private> | |||
</Reference> | |||
<Reference Include="Mono.Cecil.Pdb, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net35\Mono.Cecil.Pdb.dll</HintPath> | |||
<Private>False</Private> | |||
</Reference> | |||
<Reference Include="Mono.Cecil.Rocks, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\Mono.Cecil.0.9.6.4\lib\net35\Mono.Cecil.Rocks.dll</HintPath> | |||
<Private>False</Private> | |||
</Reference> | |||
<Reference Include="System" /> | |||
<Reference Include="System.Core" /> | |||
<Reference Include="System.Xml.Linq" /> | |||
@@ -74,12 +61,12 @@ | |||
<Compile Include="Properties\Resources.Designer.cs"> | |||
<AutoGen>True</AutoGen> | |||
<DependentUpon>Resources.resx</DependentUpon> | |||
<DesignTime>True</DesignTime> | |||
</Compile> | |||
<EmbeddedResource Include="Resources.resx"> | |||
<Generator>ResXFileCodeGenerator</Generator> | |||
<LastGenOutput>Resources.Designer.cs</LastGenOutput> | |||
</EmbeddedResource> | |||
<None Include="packages.config" /> | |||
<None Include="Properties\Settings.settings"> | |||
<Generator>SettingsSingleFileGenerator</Generator> | |||
<LastGenOutput>Settings.Designer.cs</LastGenOutput> | |||
@@ -91,7 +78,7 @@ | |||
</Compile> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="Resources\DirectToRift.exe" /> | |||
<Content Include="syringe.ico" /> | |||
</ItemGroup> | |||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
@@ -3,12 +3,10 @@ using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text.RegularExpressions; | |||
using System.Windows.Forms; | |||
using Mono.Cecil; | |||
using Mono.Cecil.Cil; | |||
using System.Threading; | |||
namespace QuietLauncher | |||
namespace Launcher | |||
{ | |||
static class Program | |||
{ | |||
@@ -29,10 +27,12 @@ namespace QuietLauncher | |||
try { | |||
var execPath = Application.ExecutablePath; | |||
var fileName = Path.GetFileNameWithoutExtension(execPath); | |||
if (fileName.IndexOf("VR") == -1 && fileName.IndexOf("_") == -1) return; | |||
if (fileName.IndexOf("VR") == -1 && fileName.IndexOf("_") == -1) | |||
{ | |||
Fail("File not named correctly!"); | |||
} | |||
bool vrMode = fileName.IndexOf("VR") > 0; | |||
bool directMode = Application.ExecutablePath.EndsWith("_DirectToRift.exe"); | |||
string baseName = execPath.Substring(0, vrMode | |||
? execPath.LastIndexOf("VR") | |||
: execPath.LastIndexOf("_")); | |||
@@ -42,70 +42,9 @@ namespace QuietLauncher | |||
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 | |||
} | |||
} | |||
if (vrMode) args.Add("--vr"); | |||
EnsureIPA(executable); | |||
StartGame(executable, args.ToArray()); | |||
} | |||
else | |||
{ | |||
@@ -117,182 +56,44 @@ namespace QuietLauncher | |||
} | |||
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) | |||
private static void EnsureIPA(string executable) | |||
{ | |||
foreach (var @ref in module.AssemblyReferences) | |||
var processStart = new ProcessStartInfo("IPA.exe", EncodeParameterArgument(executable) + " --nowait"); | |||
processStart.UseShellExecute = false; | |||
processStart.CreateNoWindow = true; | |||
processStart.RedirectStandardError = true; | |||
var process = Process.Start(processStart); | |||
process.WaitForExit(); | |||
if(process.ExitCode != 0) | |||
{ | |||
if (@ref.Name == "IllusionInjector") return true; | |||
Fail(process.StandardError.ReadToEnd()); | |||
} | |||
return false; | |||
} | |||
private static bool IsVirtualized(ModuleDefinition module) | |||
private static void StartGame(string executable, string[] args) | |||
{ | |||
return module.GetTypes().SelectMany(t => t.Methods.Where(m => m.Name == "Awake")).All(m => m.IsVirtual); | |||
var arguments = string.Join(" ", args.ToArray()); | |||
Process.Start(executable, arguments); | |||
} | |||
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) | |||
/// <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 | |||
/// to come through</returns> | |||
private static string EncodeParameterArgument(string original) | |||
{ | |||
return ENTRY_TYPES.Contains(type.Name); | |||
if (string.IsNullOrEmpty(original)) | |||
return original; | |||
string value = Regex.Replace(original, @"(\\*)" + "\"", @"$1\$0"); | |||
value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\""); | |||
return value; | |||
} | |||
} | |||
@@ -5,11 +5,11 @@ 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: AssemblyTitle("Launcher")] | |||
[assembly: AssemblyDescription("Rename to [EXE]_Patched.exe or [EXE]VR.exe")] | |||
[assembly: AssemblyConfiguration("")] | |||
[assembly: AssemblyCompany("")] | |||
[assembly: AssemblyProduct("QuietLauncher")] | |||
[assembly: AssemblyProduct("Launcher")] | |||
[assembly: AssemblyCopyright("Copyright © 2015")] | |||
[assembly: AssemblyTrademark("")] | |||
[assembly: AssemblyCulture("")] | |||
@@ -1,17 +1,17 @@ | |||
//------------------------------------------------------------------------------ | |||
// <auto-generated> | |||
// This code was generated by a tool. | |||
// Runtime Version:4.0.30319.34209 | |||
// Runtime Version:4.0.30319.42000 | |||
// | |||
// Changes to this file may cause incorrect behavior and will be lost if | |||
// the code is regenerated. | |||
// </auto-generated> | |||
//------------------------------------------------------------------------------ | |||
namespace QuietLauncher.Properties | |||
{ | |||
namespace Launcher.Properties { | |||
using System; | |||
/// <summary> | |||
/// A strongly-typed resource class, for looking up localized strings, etc. | |||
/// </summary> | |||
@@ -22,48 +22,40 @@ namespace QuietLauncher.Properties | |||
[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 | |||
{ | |||
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() | |||
{ | |||
internal Resources() { | |||
} | |||
/// <summary> | |||
/// Returns the cached ResourceManager instance used by this class. | |||
/// </summary> | |||
[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); | |||
internal static global::System.Resources.ResourceManager ResourceManager { | |||
get { | |||
if (object.ReferenceEquals(resourceMan, null)) { | |||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Launcher.Properties.Resources", typeof(Resources).Assembly); | |||
resourceMan = temp; | |||
} | |||
return resourceMan; | |||
} | |||
} | |||
/// <summary> | |||
/// Overrides the current thread's CurrentUICulture property for all | |||
/// resource lookups using this strongly typed resource class. | |||
/// </summary> | |||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | |||
internal static global::System.Globalization.CultureInfo Culture | |||
{ | |||
get | |||
{ | |||
internal static global::System.Globalization.CultureInfo Culture { | |||
get { | |||
return resourceCulture; | |||
} | |||
set | |||
{ | |||
set { | |||
resourceCulture = value; | |||
} | |||
} | |||
@@ -1,28 +1,24 @@ | |||
//------------------------------------------------------------------------------ | |||
// <auto-generated> | |||
// This code was generated by a tool. | |||
// Runtime Version:4.0.30319.34209 | |||
// Runtime Version:4.0.30319.42000 | |||
// | |||
// Changes to this file may cause incorrect behavior and will be lost if | |||
// the code is regenerated. | |||
// </auto-generated> | |||
//------------------------------------------------------------------------------ | |||
namespace QuietLauncher.Properties | |||
{ | |||
namespace Launcher.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 | |||
{ | |||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.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 | |||
{ | |||
public static Settings Default { | |||
get { | |||
return defaultInstance; | |||
} | |||
} | |||
@@ -1,14 +1,14 @@ | |||
//------------------------------------------------------------------------------ | |||
// <auto-generated> | |||
// This code was generated by a tool. | |||
// Runtime Version:4.0.30319.34209 | |||
// Runtime Version:4.0.30319.42000 | |||
// | |||
// Changes to this file may cause incorrect behavior and will be lost if | |||
// the code is regenerated. | |||
// </auto-generated> | |||
//------------------------------------------------------------------------------ | |||
namespace QuietLauncher { | |||
namespace Launcher { | |||
using System; | |||
@@ -39,7 +39,7 @@ namespace QuietLauncher { | |||
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); | |||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Launcher.Resources", typeof(Resources).Assembly); | |||
resourceMan = temp; | |||
} | |||
return resourceMan; | |||
@@ -59,15 +59,5 @@ namespace QuietLauncher { | |||
resourceCulture = value; | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized resource of type System.Byte[]. | |||
/// </summary> | |||
internal static byte[] DirectToRift { | |||
get { | |||
object obj = ResourceManager.GetObject("DirectToRift", resourceCulture); | |||
return ((byte[])(obj)); | |||
} | |||
} | |||
} | |||
} |
@@ -118,7 +118,4 @@ | |||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</resheader> | |||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> | |||
<data name="DirectToRift" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||
<value>Resources\DirectToRift.exe;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</data> | |||
</root> |