@@ -1,9 +1,5 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Xunit; | |||
namespace IPA.Tests | |||
@@ -26,7 +22,7 @@ namespace IPA.Tests | |||
// Not-flat -> Not-Flat | |||
[InlineData("native/x86/from.dll", "native/x86/to.dll", "native", false, new string[] { "native/x86/to.dll" })] | |||
[InlineData("native/x86_64/from.dll", "native/x86_64/to.dll", "native", false, new string[] { "native/x86_64/to.dll" })] | |||
[InlineData("native/x86_64/from.dll", "native/x86_64/to.dll", "native", false, new string[] { "native/x86_64/to.dll" })] | |||
public void CopiesCorrectly(string from, string to, string nativeFolder, bool isFlat, string[] expected) | |||
{ | |||
@@ -1,9 +1,5 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Threading.Tasks; | |||
using Xunit; | |||
namespace IPA.Tests | |||
@@ -1,9 +1,6 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Reflection; | |||
using System.Text; | |||
namespace IPA | |||
{ | |||
@@ -37,7 +34,7 @@ namespace IPA | |||
public static PatchContext Create(String[] args) | |||
{ | |||
var context = new PatchContext(); | |||
context.Args = args; | |||
context.Executable = args[0]; | |||
context.ProjectRoot = new FileInfo(context.Executable).Directory.FullName; | |||
@@ -49,7 +46,7 @@ namespace IPA | |||
context.ProjectName = Path.GetFileNameWithoutExtension(context.Executable); | |||
context.DataPathDst = Path.Combine(context.ProjectRoot, context.ProjectName + "_Data"); | |||
context.ManagedPath = Path.Combine(context.DataPathDst, "Managed"); | |||
context.EngineFile = DetermineEngineFile(context.ManagedPath, "UnityEngine.CoreModule.dll", "UnityEngine.dll"); | |||
context.EngineFile = DetermineEngineFile(context.ManagedPath, "UnityEngine.CoreModule.dll", "UnityEngine.dll"); | |||
context.AssemblyFile = Path.Combine(context.ManagedPath, "Assembly-Csharp.dll"); | |||
context.BackupPath = Path.Combine(Path.Combine(context.IPARoot, "Backups"), context.ProjectName); | |||
context.ShortcutPath = Path.Combine(context.ProjectRoot, $"{context.ProjectName} (Patch & Launch)") + ".lnk"; | |||
@@ -61,10 +58,10 @@ namespace IPA | |||
private static string DetermineEngineFile(string basePath, params string[] candidates) | |||
{ | |||
foreach(var candidate in candidates) | |||
foreach (var candidate in candidates) | |||
{ | |||
var fullPath = Path.Combine(basePath, candidate); | |||
if(File.Exists(fullPath)) | |||
if (File.Exists(fullPath)) | |||
{ | |||
return fullPath; | |||
} | |||
@@ -1,9 +1,5 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using System.Text.RegularExpressions; | |||
namespace IPA.Patcher | |||
{ | |||
@@ -22,11 +18,11 @@ namespace IPA.Patcher | |||
{ | |||
return FindLatestBackup(context) != null; | |||
} | |||
public static bool Restore(PatchContext context) | |||
{ | |||
var backup = FindLatestBackup(context); | |||
if(backup != null) | |||
if (backup != null) | |||
{ | |||
backup.Restore(); | |||
backup.Delete(); | |||
@@ -1,9 +1,6 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Collections.Specialized; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
namespace IPA.Patcher | |||
{ | |||
@@ -30,13 +27,14 @@ namespace IPA.Patcher | |||
_Context = context; | |||
_BackupPath = new DirectoryInfo(Path.Combine(_Context.BackupPath, Name)); | |||
} | |||
public static BackupUnit FromDirectory(DirectoryInfo directory, PatchContext context) | |||
{ | |||
var unit = new BackupUnit(context, directory.Name); | |||
// Parse directory | |||
foreach(var file in directory.GetFiles("*", SearchOption.AllDirectories)) { | |||
foreach (var file in directory.GetFiles("*", SearchOption.AllDirectories)) | |||
{ | |||
var relativePath = file.FullName.Substring(directory.FullName.Length + 1); | |||
unit._Files.Add(relativePath); | |||
} | |||
@@ -60,7 +58,7 @@ namespace IPA.Patcher | |||
/// <param name="path"></param> | |||
public void Add(FileInfo file) | |||
{ | |||
if(!file.FullName.StartsWith(_Context.ProjectRoot)) | |||
if (!file.FullName.StartsWith(_Context.ProjectRoot)) | |||
{ | |||
Console.Error.WriteLine("Invalid file path for backup! {0}", file); | |||
return; | |||
@@ -68,8 +66,8 @@ namespace IPA.Patcher | |||
var relativePath = file.FullName.Substring(_Context.ProjectRoot.Length + 1); | |||
var backupPath = new FileInfo(Path.Combine(_BackupPath.FullName, relativePath)); | |||
if(_Files.Contains(relativePath)) | |||
if (_Files.Contains(relativePath)) | |||
{ | |||
Console.WriteLine("Skipping backup of {0}", relativePath); | |||
return; | |||
@@ -81,7 +79,8 @@ namespace IPA.Patcher | |||
if (file.Exists) | |||
{ | |||
file.CopyTo(backupPath.FullName); | |||
} else | |||
} | |||
else | |||
{ | |||
// Make empty file | |||
backupPath.Create().Close(); | |||
@@ -96,7 +95,7 @@ namespace IPA.Patcher | |||
/// </summary> | |||
public void Restore() | |||
{ | |||
foreach(var relativePath in _Files) | |||
foreach (var relativePath in _Files) | |||
{ | |||
Console.WriteLine("Restoring {0}", relativePath); | |||
// Original version | |||
@@ -110,15 +109,18 @@ namespace IPA.Patcher | |||
Console.WriteLine(" {0} => {1}", backupFile.FullName, target.FullName); | |||
target.Directory.Create(); | |||
backupFile.CopyTo(target.FullName, true); | |||
} else | |||
} | |||
else | |||
{ | |||
Console.WriteLine(" x {0}", target.FullName); | |||
if(target.Exists) | |||
if (target.Exists) | |||
{ | |||
target.Delete(); | |||
} | |||
} | |||
} else { | |||
} | |||
else | |||
{ | |||
Console.Error.WriteLine("Backup not found!"); | |||
} | |||
} | |||
@@ -1,14 +1,13 @@ | |||
using Mono.Cecil; | |||
using Mono.Cecil.Cil; | |||
using System; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using Mono.Cecil; | |||
using Mono.Cecil.Cil; | |||
namespace IPA.Patcher | |||
{ | |||
class PatchedModule | |||
internal class PatchedModule | |||
{ | |||
private static readonly string[] ENTRY_TYPES = { "Input", "Display" }; | |||
@@ -36,7 +35,7 @@ namespace IPA.Patcher | |||
{ | |||
AssemblyResolver = resolver, | |||
}; | |||
_Module = ModuleDefinition.ReadModule(_File.FullName, parameters); | |||
} | |||
@@ -46,7 +45,8 @@ namespace IPA.Patcher | |||
{ | |||
foreach (var @ref in _Module.AssemblyReferences) | |||
{ | |||
if (@ref.Name == "IllusionInjector") return true; | |||
if (@ref.Name == "IllusionInjector") | |||
return true; | |||
} | |||
return false; | |||
} | |||
@@ -61,18 +61,19 @@ namespace IPA.Patcher | |||
_Module.AssemblyReferences.Add(nameReference); | |||
int patched = 0; | |||
foreach(var type in FindEntryTypes()) | |||
foreach (var type in FindEntryTypes()) | |||
{ | |||
if(PatchType(type, injector)) | |||
if (PatchType(type, injector)) | |||
{ | |||
patched++; | |||
} | |||
} | |||
if(patched > 0) | |||
if (patched > 0) | |||
{ | |||
_Module.Write(_File.FullName); | |||
} else | |||
} | |||
else | |||
{ | |||
throw new Exception("Could not find any entry type!"); | |||
} | |||
@@ -1,13 +1,11 @@ | |||
using Mono.Cecil; | |||
using System; | |||
using System.Collections.Generic; | |||
using System; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
using Mono.Cecil; | |||
namespace IPA.Patcher | |||
{ | |||
class VirtualizedModule | |||
internal class VirtualizedModule | |||
{ | |||
private const string ENTRY_TYPE = "Display"; | |||
@@ -38,7 +36,7 @@ namespace IPA.Patcher | |||
_Module = ModuleDefinition.ReadModule(_File.FullName, parameters); | |||
} | |||
/// <summary> | |||
/// | |||
/// </summary> | |||
@@ -55,17 +53,20 @@ namespace IPA.Patcher | |||
private void VirtualizeType(TypeDefinition type) | |||
{ | |||
if(type.IsSealed) | |||
if (type.IsSealed) | |||
{ | |||
// Unseal | |||
type.IsSealed = false; | |||
} | |||
if (type.IsInterface) return; | |||
if (type.IsAbstract) 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.Name == "SceneControl" || type.Name == "ConfigUI") | |||
return; | |||
Console.WriteLine("Virtualizing {0}", type.Name); | |||
// Take care of sub types | |||
@@ -97,7 +98,8 @@ namespace IPA.Patcher | |||
foreach (var field in type.Fields) | |||
{ | |||
if (field.IsPrivate) field.IsFamily = true; | |||
if (field.IsPrivate) | |||
field.IsFamily = true; | |||
} | |||
} | |||
@@ -106,7 +108,8 @@ namespace IPA.Patcher | |||
get | |||
{ | |||
var awakeMethods = _Module.GetTypes().SelectMany(t => t.Methods.Where(m => m.Name == "Awake")); | |||
if (awakeMethods.Count() == 0) return false; | |||
if (awakeMethods.Count() == 0) | |||
return false; | |||
return ((float)awakeMethods.Count(m => m.IsVirtual) / awakeMethods.Count()) > 0.5f; | |||
} | |||
@@ -1,29 +1,28 @@ | |||
using IPA.Patcher; | |||
using System; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Reflection; | |||
using System.Runtime.InteropServices; | |||
using System.Text; | |||
using System.Text.RegularExpressions; | |||
using System.Windows.Forms; | |||
using IPA.Patcher; | |||
namespace IPA | |||
{ | |||
public class Program | |||
{ | |||
public enum Architecture { | |||
public enum Architecture | |||
{ | |||
x86, | |||
x64, | |||
Unknown | |||
} | |||
static void Main(string[] args) | |||
private static void Main(string[] args) | |||
{ | |||
if(args.Length < 1 || !args[0].EndsWith(".exe")) | |||
if (args.Length < 1 || !args[0].EndsWith(".exe")) | |||
{ | |||
Fail("Drag an (executable) file on the exe!"); | |||
} | |||
@@ -44,7 +43,8 @@ namespace IPA | |||
Install(context); | |||
StartIfNeedBe(context); | |||
} | |||
} catch(Exception e) | |||
} | |||
catch (Exception e) | |||
{ | |||
Fail(e.Message); | |||
} | |||
@@ -52,7 +52,8 @@ namespace IPA | |||
private static void Validate(PatchContext c) | |||
{ | |||
if (!File.Exists(c.LauncherPathSrc)) Fail("Couldn't find DLLs! Make sure you extracted all contents of the release archive."); | |||
if (!File.Exists(c.LauncherPathSrc)) | |||
Fail("Couldn't find DLLs! Make sure you extracted all contents of the release archive."); | |||
if (!Directory.Exists(c.DataPathDst) || !File.Exists(c.EngineFile)) | |||
{ | |||
Fail("Game does not seem to be a Unity project. Could not find the libraries to patch."); | |||
@@ -74,8 +75,8 @@ namespace IPA | |||
Console.WriteLine("Architecture: {0}", architecture); | |||
CopyAll(new DirectoryInfo(context.DataPathSrc), new DirectoryInfo(context.DataPathDst), force, backup, | |||
(from, to) => NativePluginInterceptor(from, to, new DirectoryInfo(nativePluginFolder), isFlat, architecture) ); | |||
CopyAll(new DirectoryInfo(context.DataPathSrc), new DirectoryInfo(context.DataPathDst), force, backup, | |||
(from, to) => NativePluginInterceptor(from, to, new DirectoryInfo(nativePluginFolder), isFlat, architecture)); | |||
Console.WriteLine("Successfully updated files!"); | |||
@@ -109,9 +110,9 @@ namespace IPA | |||
} | |||
// Creating shortcut | |||
if(!File.Exists(context.ShortcutPath)) | |||
if (!File.Exists(context.ShortcutPath)) | |||
{ | |||
Console.Write("Creating shortcut to IPA ({0})... ", context.IPA); | |||
Console.Write("Creating shortcut to IPA ({0})... ", context.IPA); | |||
try | |||
{ | |||
Shortcut.Create( | |||
@@ -124,7 +125,8 @@ namespace IPA | |||
iconPath: context.Executable | |||
); | |||
Console.WriteLine("Created"); | |||
} catch (Exception e) | |||
} | |||
catch (Exception e) | |||
{ | |||
Console.Error.WriteLine("Failed to create shortcut, but game was patched!"); | |||
} | |||
@@ -145,15 +147,16 @@ namespace IPA | |||
private static void Revert(PatchContext context) | |||
{ | |||
Console.Write("Restoring backup... "); | |||
if(BackupManager.Restore(context)) | |||
if (BackupManager.Restore(context)) | |||
{ | |||
Console.WriteLine("Done!"); | |||
} else | |||
} | |||
else | |||
{ | |||
Console.WriteLine("Already vanilla!"); | |||
} | |||
if (File.Exists(context.ShortcutPath)) | |||
{ | |||
Console.WriteLine("Deleting shortcut..."); | |||
@@ -177,7 +180,7 @@ namespace IPA | |||
argList.RemoveAt(0); | |||
if(launch) | |||
if (launch) | |||
{ | |||
Process.Start(context.Executable, Args(argList.ToArray())); | |||
} | |||
@@ -199,11 +202,13 @@ namespace IPA | |||
// 32 bit | |||
yield return new FileInfo(Path.Combine(nativePluginFolder.FullName, relevantBit.Substring("x86".Length + 1))); | |||
} | |||
else if(is64Bit && (preferredArchitecture == Architecture.x64 || preferredArchitecture == Architecture.Unknown)) | |||
else if (is64Bit && (preferredArchitecture == Architecture.x64 || preferredArchitecture == Architecture.Unknown)) | |||
{ | |||
// 64 bit | |||
yield return new FileInfo(Path.Combine(nativePluginFolder.FullName, relevantBit.Substring("x86_64".Length + 1))); | |||
} else { | |||
} | |||
else | |||
{ | |||
// Throw away | |||
yield break; | |||
} | |||
@@ -231,7 +236,7 @@ namespace IPA | |||
public static void CopyAll(DirectoryInfo source, DirectoryInfo target, bool aggressive, BackupUnit backup, Func<FileInfo, FileInfo, IEnumerable<FileInfo>> interceptor = null) | |||
{ | |||
if(interceptor == null) | |||
if (interceptor == null) | |||
{ | |||
interceptor = PassThroughInterceptor; | |||
} | |||
@@ -239,7 +244,8 @@ namespace IPA | |||
// Copy each file into the new directory. | |||
foreach (FileInfo fi in source.GetFiles()) | |||
{ | |||
foreach(var targetFile in interceptor(fi, new FileInfo(Path.Combine(target.FullName, fi.Name)))) { | |||
foreach (var targetFile in interceptor(fi, new FileInfo(Path.Combine(target.FullName, fi.Name)))) | |||
{ | |||
if (!targetFile.Exists || targetFile.LastWriteTimeUtc < fi.LastWriteTimeUtc || aggressive) | |||
{ | |||
targetFile.Directory.Create(); | |||
@@ -259,8 +265,7 @@ namespace IPA | |||
} | |||
} | |||
static void Fail(string message) | |||
private static void Fail(string message) | |||
{ | |||
Console.Error.Write("ERROR: " + message); | |||
if (!Environment.CommandLine.Contains("--nowait")) | |||
@@ -296,7 +301,7 @@ namespace IPA | |||
using (var reader = new BinaryReader(File.OpenRead(assembly))) | |||
{ | |||
var header = reader.ReadUInt16(); | |||
if(header == 0x5a4d) | |||
if (header == 0x5a4d) | |||
{ | |||
reader.BaseStream.Seek(60, SeekOrigin.Begin); // this location contains the offset for the PE header | |||
var peOffset = reader.ReadUInt32(); | |||
@@ -312,7 +317,8 @@ namespace IPA | |||
return Architecture.x64; | |||
else | |||
return Architecture.Unknown; | |||
} else | |||
} | |||
else | |||
{ | |||
// Not a supported binary | |||
return Architecture.Unknown; | |||
@@ -3,10 +3,10 @@ using System.Runtime.InteropServices; | |||
namespace IPA | |||
{ | |||
public class Shortcut | |||
public static class Shortcut | |||
{ | |||
private static Type m_type = Type.GetTypeFromProgID("WScript.Shell"); | |||
private static object m_shell = Activator.CreateInstance(m_type); | |||
private static readonly object m_shell = Activator.CreateInstance(m_type); | |||
[ComImport, TypeLibType((short)0x1040), Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B")] | |||
private interface IWshShortcut | |||
@@ -1,28 +1,26 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Linq; | |||
using System.Text; | |||
using UnityEngine; | |||
namespace IllusionInjector | |||
{ | |||
class Bootstrapper : MonoBehaviour | |||
internal class Bootstrapper : MonoBehaviour | |||
{ | |||
public event Action Destroyed = delegate {}; | |||
void Awake() | |||
public event Action Destroyed = delegate { }; | |||
private void Awake() | |||
{ | |||
if (Environment.CommandLine.Contains("--verbose") && !Screen.fullScreen) | |||
if (Environment.CommandLine.Contains("--verbose") && (!Screen.fullScreen || Environment.CommandLine.Contains("--ipa-console"))) | |||
{ | |||
Windows.GuiConsole.CreateConsole(); | |||
} | |||
} | |||
void Start() | |||
private void Start() | |||
{ | |||
Destroy(gameObject); | |||
} | |||
void OnDestroy() | |||
private void OnDestroy() | |||
{ | |||
Destroyed(); | |||
} | |||
@@ -1,20 +1,18 @@ | |||
using IllusionPlugin; | |||
using System; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using UnityEngine; | |||
using IllusionPlugin; | |||
namespace IllusionInjector | |||
{ | |||
public class CompositePlugin : IPlugin | |||
{ | |||
IEnumerable<IPlugin> plugins; | |||
private readonly IEnumerable<IPlugin> plugins; | |||
private delegate void CompositeCall(IPlugin plugin); | |||
public CompositePlugin(IEnumerable<IPlugin> plugins) | |||
{ | |||
this.plugins = plugins; | |||
this.plugins = plugins; | |||
} | |||
public void OnApplicationStart() | |||
@@ -24,7 +22,7 @@ namespace IllusionInjector | |||
public void OnApplicationQuit() | |||
{ | |||
Invoke(plugin => plugin.OnApplicationQuit()); | |||
Invoke(plugin => plugin.OnApplicationQuit()); | |||
} | |||
public void OnLevelWasLoaded(int level) | |||
@@ -1,71 +1,69 @@ | |||
using System; | |||
using System.Collections; | |||
using System.Runtime.InteropServices; | |||
using System.IO; | |||
using System.Runtime.InteropServices; | |||
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); | |||
} | |||
internal 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); | |||
} | |||
} |
@@ -1,6 +1,4 @@ | |||
using System; | |||
using System.IO; | |||
using UnityEngine; | |||
using UnityEngine; | |||
namespace IllusionInjector | |||
{ | |||
@@ -1,7 +1,4 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using UnityEngine; | |||
using UnityEngine; | |||
namespace IllusionInjector | |||
{ | |||
@@ -16,7 +13,7 @@ namespace IllusionInjector | |||
return new GameObject("IPA_PluginManager").AddComponent<PluginComponent>(); | |||
} | |||
void Awake() | |||
private void Awake() | |||
{ | |||
DontDestroyOnLoad(gameObject); | |||
@@ -24,12 +21,12 @@ namespace IllusionInjector | |||
plugins.OnApplicationStart(); | |||
} | |||
void Start() | |||
private void Start() | |||
{ | |||
OnLevelWasLoaded(Application.loadedLevel); | |||
} | |||
void Update() | |||
private void Update() | |||
{ | |||
if (freshlyLoaded) | |||
{ | |||
@@ -39,32 +36,32 @@ namespace IllusionInjector | |||
plugins.OnUpdate(); | |||
} | |||
void LateUpdate() | |||
private void LateUpdate() | |||
{ | |||
plugins.OnLateUpdate(); | |||
} | |||
void FixedUpdate() | |||
private void FixedUpdate() | |||
{ | |||
plugins.OnFixedUpdate(); | |||
} | |||
void OnDestroy() | |||
private void OnDestroy() | |||
{ | |||
if (!quitting) | |||
{ | |||
Create(); | |||
} | |||
} | |||
void OnApplicationQuit() | |||
private void OnApplicationQuit() | |||
{ | |||
plugins.OnApplicationQuit(); | |||
quitting = true; | |||
} | |||
void OnLevelWasLoaded(int level) | |||
private void OnLevelWasLoaded(int level) | |||
{ | |||
plugins.OnLevelWasLoaded(level); | |||
freshlyLoaded = true; | |||
@@ -1,13 +1,11 @@ | |||
using IllusionPlugin; | |||
using System; | |||
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; | |||
using IllusionPlugin; | |||
namespace IllusionInjector | |||
{ | |||
@@ -22,7 +20,7 @@ namespace IllusionInjector | |||
{ | |||
get | |||
{ | |||
if(_Plugins == null) | |||
if (_Plugins == null) | |||
{ | |||
LoadPlugins(); | |||
} | |||
@@ -41,17 +39,18 @@ namespace IllusionInjector | |||
Console.WriteLine(exeName); | |||
_Plugins = new List<IPlugin>(); | |||
if (!Directory.Exists(pluginDirectory)) return; | |||
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("Running on Unity {0}", UnityEngine.Application.unityVersion); | |||
Console.WriteLine("Running on Unity {0} using {1}", UnityEngine.Application.unityVersion, GetFrameworkVersion()); | |||
Console.WriteLine("-----------------------------"); | |||
Console.WriteLine("Loading plugins from {0} and found {1}", pluginDirectory, _Plugins.Count); | |||
Console.WriteLine("-----------------------------"); | |||
@@ -76,11 +75,10 @@ namespace IllusionInjector | |||
foreach (Type t in assembly.GetTypes()) | |||
{ | |||
if (t.GetInterface("IPlugin") != null) | |||
if (IsValidPlugin(t)) | |||
{ | |||
try | |||
{ | |||
IPlugin pluginInstance = Activator.CreateInstance(t) as IPlugin; | |||
string[] filter = null; | |||
@@ -88,8 +86,8 @@ namespace IllusionInjector | |||
{ | |||
filter = ((IEnhancedPlugin)pluginInstance).Filter; | |||
} | |||
if(filter == null || Enumerable.Contains(filter, exeName, StringComparer.OrdinalIgnoreCase)) | |||
if (filter == null || Enumerable.Contains(filter, exeName, StringComparer.OrdinalIgnoreCase)) | |||
plugins.Add(pluginInstance); | |||
} | |||
catch (Exception e) | |||
@@ -108,6 +106,44 @@ namespace IllusionInjector | |||
return plugins; | |||
} | |||
private static bool IsValidPlugin(Type type) | |||
{ | |||
return typeof(IPlugin).IsAssignableFrom(type) | |||
&& !type.IsAbstract | |||
&& !type.IsInterface | |||
&& type.GetConstructor(Type.EmptyTypes) != null; | |||
} | |||
private static string GetFrameworkVersion() | |||
{ | |||
var version = Environment.Version.ToString(); | |||
try | |||
{ | |||
switch (version) | |||
{ | |||
case "2.0.50727.1433": | |||
return ".NET 3.5 Equivalent"; | |||
case "4.0.30319.17020": | |||
// For reasons unknown, switching to netstandard seems to set this back to .NET 4.0 | |||
var netstandard = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == "netstandard"); | |||
if (netstandard != null) | |||
return $".NET Standard {netstandard.GetName().Version.ToString(2)}"; | |||
goto default; | |||
case "4.0.30319.42000": | |||
return ".NET 4.x"; | |||
default: | |||
return $".NET Framework {version}"; | |||
} | |||
} | |||
catch | |||
{ | |||
// In case something goes wrong, return the best we can guess | |||
return version; | |||
} | |||
} | |||
public class AppInfo | |||
{ | |||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false)] | |||
@@ -1,8 +1,4 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace IllusionPlugin | |||
namespace IllusionPlugin | |||
{ | |||
public interface IEnhancedPlugin : IPlugin | |||
{ | |||
@@ -1,8 +1,4 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
namespace IllusionPlugin | |||
namespace IllusionPlugin | |||
{ | |||
/// <summary> | |||
/// Interface for generic Illusion unity plugins. Every class that implements this will be loaded if the DLL is placed at | |||
@@ -1,6 +1,4 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.IO; | |||
using System.Runtime.InteropServices; | |||
using System.Text; | |||
@@ -1,7 +1,5 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Text; | |||
namespace IllusionPlugin | |||
{ | |||
@@ -58,7 +56,7 @@ namespace IllusionPlugin | |||
return value; | |||
else if (autoSave) | |||
SetInt(section, name, defaultValue); | |||
return defaultValue; | |||
} | |||
@@ -96,7 +94,8 @@ namespace IllusionPlugin | |||
if (sVal == "1" || sVal == "0") | |||
{ | |||
return sVal == "1"; | |||
} else if (autoSave) | |||
} | |||
else if (autoSave) | |||
{ | |||
SetBool(section, name, defaultValue); | |||
} | |||
@@ -1,5 +1,4 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Linq; | |||
@@ -8,41 +7,43 @@ using System.Windows.Forms; | |||
namespace Launcher | |||
{ | |||
static class Program | |||
internal static class Program | |||
{ | |||
private static string[] TABOO_NAMES = { | |||
private static readonly string[] TABOO_NAMES = { | |||
//"Start", | |||
//"Update", | |||
//"Awake", | |||
//"OnDestroy" | |||
}; | |||
private static string[] ENTRY_TYPES = { "Display" }; | |||
private static readonly string[] ENTRY_TYPES = { "Display" }; | |||
/// <summary> | |||
/// The main entry point for the application. | |||
/// </summary> | |||
[STAThread] | |||
static void Main() | |||
private static void Main() | |||
{ | |||
try { | |||
try | |||
{ | |||
var execPath = Application.ExecutablePath; | |||
var fileName = Path.GetFileNameWithoutExtension(execPath); | |||
if (fileName.IndexOf("VR") == -1 && fileName.IndexOf("_") == -1) | |||
{ | |||
Fail("File not named correctly!"); | |||
} | |||
bool vrMode = fileName.IndexOf("VR") > 0; | |||
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(); | |||
if (vrMode) args.Add("--vr"); | |||
if (vrMode) | |||
args.Add("--vr"); | |||
EnsureIPA(executable); | |||
StartGame(executable, args.ToArray()); | |||
} | |||
@@ -50,10 +51,12 @@ namespace Launcher | |||
{ | |||
MessageBox.Show("Could not find: " + file.FullName, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); | |||
} | |||
} catch(Exception globalException) { | |||
} | |||
catch (Exception globalException) | |||
{ | |||
MessageBox.Show(globalException.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); | |||
} | |||
} | |||
private static void EnsureIPA(string executable) | |||
@@ -65,7 +68,7 @@ namespace Launcher | |||
var process = Process.Start(processStart); | |||
process.WaitForExit(); | |||
if(process.ExitCode != 0) | |||
if (process.ExitCode != 0) | |||
{ | |||
Fail(process.StandardError.ReadToEnd()); | |||
} | |||
@@ -77,7 +80,8 @@ namespace Launcher | |||
Process.Start(executable, arguments); | |||
} | |||
private static void Fail(string reason) { | |||
private static void Fail(string reason) | |||
{ | |||
throw new Exception(reason); | |||
} | |||