Browse Source

Add files via upload

pull/46/head
Michael Guedko 5 years ago
committed by GitHub
parent
commit
37d207f5b7
40 changed files with 2416 additions and 21 deletions
  1. +93
    -0
      IPA.Tests/IPA.Tests.csproj
  2. +39
    -0
      IPA.Tests/ProgramTest.cs
  3. +36
    -0
      IPA.Tests/Properties/AssemblyInfo.cs
  4. +37
    -0
      IPA.Tests/ShortcutTest.cs
  5. +11
    -0
      IPA.Tests/packages.config
  6. +99
    -0
      IPA/IPA.csproj
  7. +63
    -0
      IPA/PatchContext.cs
  8. +39
    -0
      IPA/Patcher/BackupManager.cs
  9. +128
    -0
      IPA/Patcher/BackupUnit.cs
  10. +99
    -0
      IPA/Patcher/Patcher.cs
  11. +115
    -0
      IPA/Patcher/Virtualizer.cs
  12. +365
    -0
      IPA/Program.cs
  13. +36
    -0
      IPA/Properties/AssemblyInfo.cs
  14. +61
    -0
      IPA/Shortcut.cs
  15. BIN
      IPA/favicon.ico
  16. +1
    -0
      IPA/obj/Debug/IPA.csproj.CoreCompileInputs.cache
  17. +4
    -0
      IPA/packages.config
  18. +30
    -0
      IllusionInjector/Bootstrapper.cs
  19. +109
    -0
      IllusionInjector/CompositePlugin.cs
  20. +71
    -0
      IllusionInjector/ConsoleWindow.cs
  21. +66
    -0
      IllusionInjector/IllusionInjector.csproj
  22. +25
    -0
      IllusionInjector/Injector.cs
  23. +74
    -0
      IllusionInjector/PluginComponent.cs
  24. +128
    -0
      IllusionInjector/PluginManager.cs
  25. +36
    -0
      IllusionInjector/Properties/AssemblyInfo.cs
  26. +1
    -0
      IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache
  27. +21
    -21
      LICENSE
  28. +91
    -0
      Launcher/Launcher.csproj
  29. +100
    -0
      Launcher/Program.cs
  30. +36
    -0
      Launcher/Properties/AssemblyInfo.cs
  31. +63
    -0
      Launcher/Properties/Resources.Designer.cs
  32. +117
    -0
      Launcher/Properties/Resources.resx
  33. +26
    -0
      Launcher/Properties/Settings.Designer.cs
  34. +7
    -0
      Launcher/Properties/Settings.settings
  35. +63
    -0
      Launcher/Resources.Designer.cs
  36. +121
    -0
      Launcher/Resources.resx
  37. +1
    -0
      Launcher/obj/Debug/Launcher.csproj.CoreCompileInputs.cache
  38. +4
    -0
      Launcher/packages.config
  39. BIN
      Launcher/syringe.ico
  40. BIN
      Libs/UnityEngine.dll

+ 93
- 0
IPA.Tests/IPA.Tests.csproj View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props" Condition="Exists('..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C66092B0-5C1E-44E9-B524-E0E8E1425379}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>IPA.Tests</RootNamespace>
<AssemblyName>IPA.Tests</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.assert, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.core, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="xunit.execution.desktop, Version=2.1.0.3179, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL">
<HintPath>..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="ProgramTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ShortcutTest.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IPA\IPA.csproj">
<Project>{14092533-98bb-40a4-9afc-27bb75672a70}</Project>
<Name>IPA</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props'))" />
</Target>
<!-- 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.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

+ 39
- 0
IPA.Tests/ProgramTest.cs View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace IPA.Tests
{
public class ProgramTest
{
[Theory]
// Unrelated path
[InlineData("test/from.dll", "test/to.dll", "native", false, new string[] { "test/to.dll" })]
// Flat -> Not-Flat
[InlineData("native/from.dll", "native/to.dll", "native", false, new string[] { "native/x86/to.dll", "native/x86_64/to.dll" })]
// Flat -> Flat
[InlineData("native/from.dll", "native/to.dll", "native", true, new string[] { "native/to.dll" })]
// Not-Flat -> Flat
[InlineData("native/x86/from.dll", "native/x86/to.dll", "native", true, new string[] { })]
[InlineData("native/x86_64/from.dll", "native/x86_64/to.dll", "native", true, new string[] { "native/to.dll" })]
// 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" })]
public void CopiesCorrectly(string from, string to, string nativeFolder, bool isFlat, string[] expected)
{
var outcome = Program.NativePluginInterceptor(new FileInfo(from), new FileInfo(to), new DirectoryInfo(nativeFolder), isFlat).Select(f => f.FullName).ToList();
var expectedPaths = expected.Select(e => new FileInfo(e)).Select(f => f.FullName).ToList();
Assert.Equal(expectedPaths, outcome);
}
}
}

+ 36
- 0
IPA.Tests/Properties/AssemblyInfo.cs View File

@ -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.Tests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("IPA.Tests")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[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("c66092b0-5c1e-44e9-b524-e0e8e1425379")]
// 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")]

+ 37
- 0
IPA.Tests/ShortcutTest.cs View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace IPA.Tests
{
public class ShortcutTest
{
[Fact]
public void CanDealWithEmptyFiles()
{
Shortcut.Create(".lnk", "", "", "", "", "", "");
}
[Fact]
public void CanDealWithLongFiles()
{
Shortcut.Create(".lnk", Path.Combine(Path.GetTempPath(), string.Join("_", new string[500])), "", "", "", "", "");
}
[Fact]
public void CantDealWithNull()
{
Assert.Throws<ArgumentException>(() => Shortcut.Create(".lnk", null, "", "", "", "", ""));
}
[Fact]
public void CanDealWithWeirdCharacters()
{
Shortcut.Create(".lnk", "äöü", "", "", "", "", "");
}
}
}

+ 11
- 0
IPA.Tests/packages.config View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="xunit" version="2.1.0" targetFramework="net452" />
<package id="xunit.abstractions" version="2.0.0" targetFramework="net452" />
<package id="xunit.assert" version="2.1.0" targetFramework="net452" />
<package id="xunit.core" version="2.1.0" targetFramework="net452" />
<package id="xunit.extensibility.core" version="2.1.0" targetFramework="net452" />
<package id="xunit.extensibility.execution" version="2.1.0" targetFramework="net452" />
<package id="xunit.runner.console" version="2.1.0" targetFramework="net452" />
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net452" />
</packages>

+ 99
- 0
IPA/IPA.csproj View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{14092533-98BB-40A4-9AFC-27BB75672A70}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>IPA</RootNamespace>
<AssemblyName>IPA</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<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.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="PatchContext.cs" />
<Compile Include="Patcher\BackupManager.cs" />
<Compile Include="Patcher\BackupUnit.cs" />
<Compile Include="Patcher\Patcher.cs" />
<Compile Include="Patcher\Virtualizer.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Shortcut.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="favicon.ico" />
</ItemGroup>
<ItemGroup>
<Folder Include="IPA\Fallback\" />
</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.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<Target Name="AfterBuild">
<Message Text="Packing..." Importance="normal" />
<ItemGroup>
<Launcher Include="$(SolutionDir)Launcher\$(OutDir)Launcher.exe" />
<Dlls Include="$(SolutionDir)IllusionInjector\$(OutDir)**\*" />
</ItemGroup>
<Copy SourceFiles="@(Dlls)" DestinationFolder="$(OutputPath)IPA\Data\Managed" />
<Copy SourceFiles="@(Launcher)" DestinationFolder="$(OutputPath)IPA" />
</Target>
</Project>

+ 63
- 0
IPA/PatchContext.cs View File

@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace IPA
{
public class PatchContext
{
/// <summary>
/// Gets the filename of the executable.
/// </summary>
public string Executable { get; private set; }
/// <summary>
/// Gets the path to the launcher executable (in the IPA folder)
/// </summary>
public string LauncherPathSrc { get; private set; }
public string DataPathSrc { get; private set; }
public string PluginsFolder { get; private set; }
public string ProjectName { get; private set; }
public string DataPathDst { get; private set; }
public string ManagedPath { get; private set; }
public string EngineFile { get; private set; }
public string AssemblyFile { get; private set; }
public string[] Args { get; private set; }
public string ProjectRoot { get; private set; }
public string IPARoot { get; private set; }
public string ShortcutPath { get; private set; }
public string IPA { get; private set; }
public string BackupPath { get; private set; }
private PatchContext() { }
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;
context.IPARoot = Path.Combine(context.ProjectRoot, "IPA");
context.IPA = Assembly.GetExecutingAssembly().Location ?? Path.Combine(context.ProjectRoot, "IPA.exe");
context.LauncherPathSrc = Path.Combine(context.IPARoot, "Launcher.exe");
context.DataPathSrc = Path.Combine(context.IPARoot, "Data");
context.PluginsFolder = Path.Combine(context.ProjectRoot, "Plugins");
context.ProjectName = Path.GetFileNameWithoutExtension(context.Executable);
context.DataPathDst = Path.Combine(context.ProjectRoot, context.ProjectName + "_Data");
context.ManagedPath = Path.Combine(context.DataPathDst, "Managed");
context.EngineFile = Path.Combine(context.ManagedPath, "UnityEngineCore.dll");
context.AssemblyFile = Path.Combine(context.ManagedPath, "Assembly-CSharp.dll");
context.BackupPath = Path.Combine(Path.Combine(context.IPARoot, "Backups"), context.ProjectName);
string shortcutName = string.Format("{0} (Patch & Launch)", context.ProjectName);
context.ShortcutPath = Path.Combine(context.ProjectRoot, shortcutName) + ".lnk";
Directory.CreateDirectory(context.BackupPath);
return context;
}
}
}

+ 39
- 0
IPA/Patcher/BackupManager.cs View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace IPA.Patcher
{
public class BackupManager
{
public static BackupUnit FindLatestBackup(PatchContext context)
{
return new DirectoryInfo(context.BackupPath)
.GetDirectories()
.OrderByDescending(p => p.Name)
.Select(p => BackupUnit.FromDirectory(p, context))
.FirstOrDefault();
}
public static bool HasBackup(PatchContext context)
{
return FindLatestBackup(context) != null;
}
public static bool Restore(PatchContext context)
{
var backup = FindLatestBackup(context);
if(backup != null)
{
backup.Restore();
backup.Delete();
return true;
}
return false;
}
}
}

+ 128
- 0
IPA/Patcher/BackupUnit.cs View File

@ -0,0 +1,128 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
namespace IPA.Patcher
{
/// <summary>
/// A unit for backup. WIP.
/// </summary>
public class BackupUnit
{
public string Name { get; private set; }
private DirectoryInfo _BackupPath;
private PatchContext _Context;
private List<string> _Files = new List<string>();
public BackupUnit(PatchContext context) : this(context, DateTime.Now.ToString("yyyy-MM-dd_h-mm-ss"))
{
}
private BackupUnit(PatchContext context, string name)
{
Name = name;
_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)) {
var relativePath = file.FullName.Substring(directory.FullName.Length + 1);
unit._Files.Add(relativePath);
}
return unit;
}
public void Add(string file)
{
Add(new FileInfo(file));
}
internal void Delete()
{
_BackupPath.Delete(true);
}
/// <summary>
/// Adds a file to the list of changed files and backups it.
/// </summary>
/// <param name="path"></param>
public void Add(FileInfo file)
{
if(!file.FullName.StartsWith(_Context.ProjectRoot))
{
Console.Error.WriteLine("Invalid file path for backup! {0}", file);
return;
}
var relativePath = file.FullName.Substring(_Context.ProjectRoot.Length + 1);
var backupPath = new FileInfo(Path.Combine(_BackupPath.FullName, relativePath));
if(_Files.Contains(relativePath))
{
Console.WriteLine("Skipping backup of {0}", relativePath);
return;
}
// Copy over
backupPath.Directory.Create();
if (file.Exists)
{
file.CopyTo(backupPath.FullName);
} else
{
// Make empty file
backupPath.Create().Close();
}
// Add to list
_Files.Add(relativePath);
}
/// <summary>
/// Reverts the changes made in this unit.
/// </summary>
public void Restore()
{
foreach(var relativePath in _Files)
{
Console.WriteLine("Restoring {0}", relativePath);
// Original version
var backupFile = new FileInfo(Path.Combine(_BackupPath.FullName, relativePath));
var target = new FileInfo(Path.Combine(_Context.ProjectRoot, relativePath));
if (backupFile.Exists)
{
if (backupFile.Length > 0)
{
Console.WriteLine(" {0} => {1}", backupFile.FullName, target.FullName);
target.Directory.Create();
backupFile.CopyTo(target.FullName, true);
} else
{
Console.WriteLine(" x {0}", target.FullName);
if(target.Exists)
{
target.Delete();
}
}
} else {
Console.Error.WriteLine("Backup not found!");
}
}
}
}
}

+ 99
- 0
IPA/Patcher/Patcher.cs View File

@ -0,0 +1,99 @@
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 static readonly string[] ENTRY_TYPES = { "Input", "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");
var injector = ModuleDefinition.ReadModule(injectorPath);
_Module.AssemblyReferences.Add(nameReference);
int patched = 0;
foreach(var type in FindEntryTypes())
{
if(PatchType(type, injector))
{
patched++;
}
}
if(patched > 0)
{
_Module.Write(_File.FullName);
} else
{
throw new Exception("Could not find any entry type!");
}
}
private bool PatchType(TypeDefinition targetType, ModuleDefinition injector)
{
var targetMethod = targetType.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic);
if (targetMethod != null)
{
var methodReference = _Module.Import(injector.GetType("IllusionInjector.Injector").Methods.First(m => m.Name == "Inject"));
targetMethod.Body.Instructions.Insert(0, Instruction.Create(OpCodes.Call, methodReference));
return true;
}
return false;
}
private IEnumerable<TypeDefinition> FindEntryTypes()
{
return _Module.GetTypes().Where(m => ENTRY_TYPES.Contains(m.Name));
}
}
}

+ 115
- 0
IPA/Patcher/Virtualizer.cs View File

@ -0,0 +1,115 @@
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);
}
_Module.Write(_File.FullName);
}
private void VirtualizeType(TypeDefinition type)
{
if(type.IsSealed)
{
// Unseal
type.IsSealed = false;
}
if (type.IsInterface) return;
if (type.IsAbstract) return;
// These two don't seem to work.
if (type.Name == "SceneControl" || type.Name == "ConfigUI") return;
Console.WriteLine("Virtualizing {0}", type.Name);
// 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
{
var awakeMethods = _Module.GetTypes().SelectMany(t => t.Methods.Where(m => m.Name == "Awake"));
if (awakeMethods.Count() == 0) return false;
return ((float)awakeMethods.Count(m => m.IsVirtual) / awakeMethods.Count()) > 0.5f;
}
}
}
}

+ 365
- 0
IPA/Program.cs View File

@ -0,0 +1,365 @@
using IPA.Patcher;
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;
namespace IPA
{
public class Program
{
public enum Architecture {
x86,
x64,
Unknown
}
static void Main(string[] args)
{
if(args.Length < 1 || !args[0].EndsWith(".exe"))
{
Fail("Drag an (executable) file on the exe!");
}
try
{
var context = PatchContext.Create(args);
bool isRevert = args.Contains("--revert") || Keyboard.IsKeyDown(Keys.LMenu);
// Sanitizing
Validate(context);
if (isRevert)
{
Revert(context);
}
else
{
Install(context);
StartIfNeedBe(context);
}
} catch(Exception e)
{
Fail(e.Message);
}
}
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 (!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.");
}
}
private static void Install(PatchContext context)
{
try
{
var backup = new BackupUnit(context);
// Copying
Console.WriteLine("Updating files... ");
var nativePluginFolder = Path.Combine(context.DataPathDst, "Plugins");
bool isFlat = Directory.Exists(nativePluginFolder) && Directory.GetFiles(nativePluginFolder).Any(f => f.EndsWith(".dll"));
bool force = !BackupManager.HasBackup(context) || context.Args.Contains("-f") || context.Args.Contains("--force");
var architecture = DetectArchitecture(context.Executable);
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) );
Console.WriteLine("Successfully updated files!");
if (!Directory.Exists(context.PluginsFolder))
{
Console.WriteLine("Creating plugins folder... ");
Directory.CreateDirectory(context.PluginsFolder);
}
// Patching
var patchedModule = PatchedModule.Load(context.EngineFile);
if (!patchedModule.IsPatched)
{
Console.Write("Patching UnityEngine.dll... ");
backup.Add(context.EngineFile);
patchedModule.Patch();
Console.WriteLine("Done!");
}
// Virtualizing
if (File.Exists(context.AssemblyFile))
{
var virtualizedModule = VirtualizedModule.Load(context.AssemblyFile);
if (!virtualizedModule.IsVirtualized)
{
Console.Write("Virtualizing Assembly-Csharp.dll... ");
backup.Add(context.AssemblyFile);
virtualizedModule.Virtualize();
Console.WriteLine("Done!");
}
}
// Creating shortcut
if(!File.Exists(context.ShortcutPath))
{
Console.Write("Creating shortcut to IPA ({0})... ", context.IPA);
try
{
Shortcut.Create(
fileName: context.ShortcutPath,
targetPath: context.IPA,
arguments: Args(context.Executable, "--launch"),
workingDirectory: context.ProjectRoot,
description: "Launches the game and makes sure it's in a patched state",
hotkey: "",
iconPath: context.Executable
);
Console.WriteLine("Created");
} catch (Exception e)
{
Console.Error.WriteLine("Failed to create shortcut, but game was patched!");
}
}
}
catch (Exception e)
{
Fail("Oops! This should not have happened.\n\n" + e);
}
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Finished!");
Console.ResetColor();
}
private static void Revert(PatchContext context)
{
Console.Write("Restoring backup... ");
if(BackupManager.Restore(context))
{
Console.WriteLine("Done!");
} else
{
Console.WriteLine("Already vanilla!");
}
if (File.Exists(context.ShortcutPath))
{
Console.WriteLine("Deleting shortcut...");
File.Delete(context.ShortcutPath);
}
Console.WriteLine("");
Console.WriteLine("--- Done reverting ---");
if (!Environment.CommandLine.Contains("--nowait"))
{
Console.WriteLine("\n\n[Press any key to quit]");
Console.ReadKey();
}
}
private static void StartIfNeedBe(PatchContext context)
{
var argList = context.Args.ToList();
bool launch = argList.Remove("--launch");
argList.RemoveAt(0);
if(launch)
{
Process.Start(context.Executable, Args(argList.ToArray()));
}
}
public static IEnumerable<FileInfo> NativePluginInterceptor(FileInfo from, FileInfo to, DirectoryInfo nativePluginFolder, bool isFlat, Architecture preferredArchitecture)
{
if (to.FullName.StartsWith(nativePluginFolder.FullName))
{
var relevantBit = to.FullName.Substring(nativePluginFolder.FullName.Length + 1);
// Goes into the plugin folder!
bool isFileFlat = !relevantBit.StartsWith("x86");
if (isFlat && !isFileFlat)
{
// Flatten structure
bool is64Bit = relevantBit.StartsWith("x86_64");
if (!is64Bit && preferredArchitecture == Architecture.x86)
{
// 32 bit
yield return new FileInfo(Path.Combine(nativePluginFolder.FullName, relevantBit.Substring("x86".Length + 1)));
}
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 {
// Throw away
yield break;
}
}
else if (!isFlat && isFileFlat)
{
// Deepen structure
yield return new FileInfo(Path.Combine(Path.Combine(nativePluginFolder.FullName, "x86"), relevantBit));
yield return new FileInfo(Path.Combine(Path.Combine(nativePluginFolder.FullName, "x86_64"), relevantBit));
}
else
{
yield return to;
}
}
else
{
yield return to;
}
}
private static IEnumerable<FileInfo> PassThroughInterceptor(FileInfo from, FileInfo to)
{
yield return to;
}
public static void CopyAll(DirectoryInfo source, DirectoryInfo target, bool aggressive, BackupUnit backup, Func<FileInfo, FileInfo, IEnumerable<FileInfo>> interceptor = null)
{
if(interceptor == null)
{
interceptor = PassThroughInterceptor;
}
// 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)))) {
if (!targetFile.Exists || targetFile.LastWriteTimeUtc < fi.LastWriteTimeUtc || aggressive)
{
targetFile.Directory.Create();
Console.WriteLine(@"Copying {0}", targetFile.FullName);
backup.Add(targetFile);
fi.CopyTo(targetFile.FullName, true);
}
}
}
// Copy each subdirectory using recursion.
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir = new DirectoryInfo(Path.Combine(target.FullName, diSourceSubDir.Name));
CopyAll(diSourceSubDir, nextTargetSubDir, aggressive, backup, interceptor);
}
}
static void Fail(string message)
{
Console.Error.Write("ERROR: " + message);
if (!Environment.CommandLine.Contains("--nowait"))
{
Console.WriteLine("\n\n[Press any key to quit]");
Console.ReadKey();
}
Environment.Exit(1);
}
public static string Args(params string[] args)
{
return string.Join(" ", args.Select(EncodeParameterArgument).ToArray());
}
/// <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>
public static string EncodeParameterArgument(string original)
{
if (string.IsNullOrEmpty(original))
return original;
string value = Regex.Replace(original, @"(\\*)" + "\"", @"$1\$0");
value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\"");
return value;
}
public static Architecture DetectArchitecture(string assembly)
{
using (var reader = new BinaryReader(File.OpenRead(assembly)))
{
var header = reader.ReadUInt16();
if(header == 0x5a4d)
{
reader.BaseStream.Seek(60, SeekOrigin.Begin); // this location contains the offset for the PE header
var peOffset = reader.ReadUInt32();
reader.BaseStream.Seek(peOffset + 4, SeekOrigin.Begin);
var machine = reader.ReadUInt16();
if (machine == 0x8664) // IMAGE_FILE_MACHINE_AMD64
return Architecture.x64;
else if (machine == 0x014c) // IMAGE_FILE_MACHINE_I386
return Architecture.x86;
else if (machine == 0x0200) // IMAGE_FILE_MACHINE_IA64
return Architecture.x64;
else
return Architecture.Unknown;
} else
{
// Not a supported binary
return Architecture.Unknown;
}
}
}
public abstract class Keyboard
{
[Flags]
private enum KeyStates
{
None = 0,
Down = 1,
Toggled = 2
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern short GetKeyState(int keyCode);
private static KeyStates GetKeyState(Keys key)
{
KeyStates state = KeyStates.None;
short retVal = GetKeyState((int)key);
//If the high-order bit is 1, the key is down
//otherwise, it is up.
if ((retVal & 0x8000) == 0x8000)
state |= KeyStates.Down;
//If the low-order bit is 1, the key is toggled.
if ((retVal & 1) == 1)
state |= KeyStates.Toggled;
return state;
}
public static bool IsKeyDown(Keys key)
{
return KeyStates.Down == (GetKeyState(key) & KeyStates.Down);
}
public static bool IsKeyToggled(Keys key)
{
return KeyStates.Toggled == (GetKeyState(key) & KeyStates.Toggled);
}
}
}
}

+ 36
- 0
IPA/Properties/AssemblyInfo.cs View File

@ -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("Illusion Plugin Architecture")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Illusion Plugin Architecture")]
[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("3.1.1.0")]
[assembly: AssemblyFileVersion("3.1.1.0")]

+ 61
- 0
IPA/Shortcut.cs View File

@ -0,0 +1,61 @@
using System;
using System.Runtime.InteropServices;
namespace IPA
{
public class Shortcut
{
private static Type m_type = Type.GetTypeFromProgID("WScript.Shell");
private static object m_shell = Activator.CreateInstance(m_type);
[ComImport, TypeLibType((short)0x1040), Guid("F935DC23-1CF0-11D0-ADB9-00C04FD58A0B")]
private interface IWshShortcut
{
[DispId(0)]
string FullName { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0)] get; }
[DispId(0x3e8)]
string Arguments { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3e8)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3e8)] set; }
[DispId(0x3e9)]
string Description { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3e9)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3e9)] set; }
[DispId(0x3ea)]
string Hotkey { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3ea)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ea)] set; }
[DispId(0x3eb)]
string IconLocation { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3eb)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3eb)] set; }
[DispId(0x3ec)]
string RelativePath { [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ec)] set; }
[DispId(0x3ed)]
string TargetPath { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3ed)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ed)] set; }
[DispId(0x3ee)]
int WindowStyle { [DispId(0x3ee)] get; [param: In] [DispId(0x3ee)] set; }
[DispId(0x3ef)]
string WorkingDirectory { [return: MarshalAs(UnmanagedType.BStr)] [DispId(0x3ef)] get; [param: In, MarshalAs(UnmanagedType.BStr)] [DispId(0x3ef)] set; }
[TypeLibFunc((short)0x40), DispId(0x7d0)]
void Load([In, MarshalAs(UnmanagedType.BStr)] string PathLink);
[DispId(0x7d1)]
void Save();
}
public static void Create(string fileName, string targetPath, string arguments, string workingDirectory, string description, string hotkey, string iconPath)
{
IWshShortcut shortcut = (IWshShortcut)m_type.InvokeMember("CreateShortcut", System.Reflection.BindingFlags.InvokeMethod, null, m_shell, new object[] { fileName });
shortcut.Description = description;
shortcut.Hotkey = hotkey;
shortcut.TargetPath = targetPath;
shortcut.WorkingDirectory = workingDirectory;
shortcut.Arguments = arguments;
if (!string.IsNullOrEmpty(iconPath))
shortcut.IconLocation = iconPath;
shortcut.Save();
}
}
}

BIN
IPA/favicon.ico View File

Before After

+ 1
- 0
IPA/obj/Debug/IPA.csproj.CoreCompileInputs.cache View File

@ -0,0 +1 @@
f7c20aca6b99cd3b62d50a05d9bdca1eb41dca2a

+ 4
- 0
IPA/packages.config View File

@ -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>

+ 30
- 0
IllusionInjector/Bootstrapper.cs View File

@ -0,0 +1,30 @@
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 Awake()
{
if (Environment.CommandLine.Contains("--verbose") && !Screen.fullScreen)
{
Windows.GuiConsole.CreateConsole();
}
}
void Start()
{
Destroy(gameObject);
}
void OnDestroy()
{
Destroyed();
}
}
}

+ 109
- 0
IllusionInjector/CompositePlugin.cs View File

@ -0,0 +1,109 @@
using IllusionPlugin;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace IllusionInjector
{
public class CompositePlugin : IPlugin
{
IEnumerable<IPlugin> plugins;
private delegate void CompositeCall(IPlugin plugin);
public CompositePlugin(IEnumerable<IPlugin> 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();
});
}
}
}

+ 71
- 0
IllusionInjector/ConsoleWindow.cs View File

@ -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);
}
}

+ 66
- 0
IllusionInjector/IllusionInjector.csproj View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{D1C61AF5-0D2D-4752-8203-1C6929025F7C}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>IllusionInjector</RootNamespace>
<AssemblyName>IllusionInjector</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="UnityEngine">
<HintPath>..\Libs\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Bootstrapper.cs" />
<Compile Include="CompositePlugin.cs" />
<Compile Include="ConsoleWindow.cs" />
<Compile Include="Injector.cs" />
<Compile Include="PluginManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PluginComponent.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IllusionPlugin\IllusionPlugin.csproj">
<Project>{e2848bfb-5432-42f4-8ae0-d2ec0cdf2f71}</Project>
<Name>IllusionPlugin</Name>
</ProjectReference>
</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.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

+ 25
- 0
IllusionInjector/Injector.cs View File

@ -0,0 +1,25 @@
using System;
using System.IO;
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>();
bootstrapper.Destroyed += Bootstrapper_Destroyed;
}
}
private static void Bootstrapper_Destroyed()
{
PluginComponent.Create();
}
}
}

+ 74
- 0
IllusionInjector/PluginComponent.cs View File

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace IllusionInjector
{
public class PluginComponent : MonoBehaviour
{
private CompositePlugin plugins;
private bool freshlyLoaded = false;
private bool quitting = false;
public static PluginComponent Create()
{
return new GameObject("IPA_PluginManager").AddComponent<PluginComponent>();
}
void Awake()
{
DontDestroyOnLoad(gameObject);
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()
{
if (!quitting)
{
Create();
}
}
void OnApplicationQuit()
{
plugins.OnApplicationQuit();
quitting = true;
}
void OnLevelWasLoaded(int level)
{
plugins.OnLevelWasLoaded(level);
freshlyLoaded = true;
}
}
}

+ 128
- 0
IllusionInjector/PluginManager.cs View File

@ -0,0 +1,128 @@
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<IPlugin> _Plugins = null;
/// <summary>
/// Gets the list of loaded plugins and loads them if necessary.
/// </summary>
public static IEnumerable<IPlugin> 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<IPlugin>();
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("-----------------------------");
Console.WriteLine("Loading plugins from {0} and found {1}", pluginDirectory, _Plugins.Count);
Console.WriteLine("-----------------------------");
foreach (var plugin in _Plugins)
{