@ -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> |
@ -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); | |||
} | |||
} | |||
} |
@ -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")] |
@ -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", "äöü", "", "", "", "", ""); | |||
} | |||
} | |||
} |
@ -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> |
@ -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> |
@ -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; | |||
} | |||
} | |||
} |
@ -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; | |||
} | |||
} | |||
} |
@ -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!"); | |||
} | |||
} | |||
} | |||
} | |||
} |
@ -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)); | |||
} | |||
} | |||
} |
@ -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; | |||
} | |||
} | |||
} | |||
} |
@ -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); | |||
} | |||
} | |||
} | |||
} |
@ -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")] |
@ -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(); | |||
} | |||
} | |||
} |
@ -0,0 +1 @@ | |||
f7c20aca6b99cd3b62d50a05d9bdca1eb41dca2a |
@ -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> |
@ -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(); | |||
} | |||
} | |||
} |
@ -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(); | |||
}); | |||
} | |||
} | |||
} |
@ -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); | |||
} | |||
} |
@ -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> |
@ -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(); | |||
} | |||
} | |||
} |
@ -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; | |||
} | |||
} | |||
} |
@ -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) | |||
{ | |||
Console.WriteLine(" {0}: {1}", plugin.Name, plugin.Version); | |||
} | |||
Console.WriteLine("-----------------------------"); | |||
} | |||
private static IEnumerable<IPlugin> LoadPluginsFromFile(string file, string exeName) | |||
{ | |||
List<IPlugin> plugins = new List<IPlugin>(); | |||
if (!File.Exists(file) || !file.EndsWith(".dll", true, null)) | |||
return plugins; | |||
try | |||
{ | |||
Assembly assembly = Assembly.LoadFrom(file); | |||
foreach (Type t in assembly.GetTypes()) | |||
{ | |||
if (t.GetInterface("IPlugin") != null) | |||
{ | |||
try | |||
{ | |||
IPlugin pluginInstance = Activator.CreateInstance(t) as IPlugin; | |||
string[] filter = null; | |||
if (pluginInstance is IEnhancedPlugin) | |||
{ | |||
filter = ((IEnhancedPlugin)pluginInstance).Filter; | |||
} | |||
if(filter == null || Enumerable.Contains(filter, exeName, StringComparer.OrdinalIgnoreCase)) | |||
plugins.Add(pluginInstance); | |||
} | |||
catch (Exception e) | |||
{ | |||
Console.WriteLine("[WARN] Could not load plugin {0} in {1}! {2}", t.FullName, Path.GetFileName(file), e); | |||
} | |||
} | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Console.WriteLine("[ERROR] Could not load {0}! {1}", Path.GetFileName(file), e); | |||
} | |||
return plugins; | |||
} | |||
public class AppInfo | |||
{ | |||
[DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = false)] | |||
private static extern int GetModuleFileName(HandleRef hModule, StringBuilder buffer, int length); | |||
private static HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero); | |||
public static string StartupPath | |||
{ | |||
get | |||
{ | |||
StringBuilder stringBuilder = new StringBuilder(260); | |||
GetModuleFileName(NullHandleRef, stringBuilder, stringBuilder.Capacity); | |||
return stringBuilder.ToString(); | |||
} | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,36 @@ | |||
using System.Reflection; | |||
using System.Runtime.CompilerServices; | |||
using System.Runtime.InteropServices; | |||
// General Information about an assembly is controlled through the following | |||
// set of attributes. Change these attribute values to modify the information | |||
// associated with an assembly. | |||
[assembly: AssemblyTitle("IllusionInjector")] | |||
[assembly: AssemblyDescription("")] | |||
[assembly: AssemblyConfiguration("")] | |||
[assembly: AssemblyCompany("")] | |||
[assembly: AssemblyProduct("IllusionInjector")] | |||
[assembly: AssemblyCopyright("Copyright © 2015")] | |||
[assembly: AssemblyTrademark("")] | |||
[assembly: AssemblyCulture("")] | |||
// Setting ComVisible to false makes the types in this assembly not visible | |||
// to COM components. If you need to access a type in this assembly from | |||
// COM, set the ComVisible attribute to true on that type. | |||
[assembly: ComVisible(false)] | |||
// The following GUID is for the ID of the typelib if this project is exposed to COM | |||
[assembly: Guid("400a540a-d21f-4609-966b-206059b6e73b")] | |||
// Version information for an assembly consists of the following four values: | |||
// | |||
// Major Version | |||
// Minor Version | |||
// Build Number | |||
// Revision | |||
// | |||
// You can specify all the values or you can default the Build and Revision Numbers | |||
// by using the '*' as shown below: | |||
// [assembly: AssemblyVersion("1.0.*")] | |||
[assembly: AssemblyVersion("1.0.0.0")] | |||
[assembly: AssemblyFileVersion("1.0.0.0")] |
@ -0,0 +1 @@ | |||
207669803f9c3f5ead7136a41944fdaa372fd294 |
@ -1,21 +1,21 @@ | |||
MIT License | |||
Copyright (c) 2018 Michael Guedko | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. | |||
MIT License | |||
Copyright (c) 2016 | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all | |||
copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
SOFTWARE. |
@ -0,0 +1,91 @@ | |||
<?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>{D1390268-F68B-4A55-B50D-EAD25756C8EF}</ProjectGuid> | |||
<OutputType>WinExe</OutputType> | |||
<AppDesignerFolder>Properties</AppDesignerFolder> | |||
<RootNamespace>Launcher</RootNamespace> | |||
<AssemblyName>Launcher</AssemblyName> | |||
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion> | |||
<FileAlignment>512</FileAlignment> | |||
</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>pdbonly</DebugType> | |||
<Optimize>true</Optimize> | |||
<OutputPath>bin\Release\</OutputPath> | |||
<DefineConstants>TRACE</DefineConstants> | |||
<ErrorReport>prompt</ErrorReport> | |||
<WarningLevel>4</WarningLevel> | |||
</PropertyGroup> | |||
<PropertyGroup> | |||
<ApplicationIcon>syringe.ico</ApplicationIcon> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="System" /> | |||
<Reference Include="System.Core" /> | |||
<Reference Include="System.Xml.Linq" /> | |||
<Reference Include="System.Data.DataSetExtensions" /> | |||
<Reference Include="System.Data" /> | |||
<Reference Include="System.Deployment" /> | |||
<Reference Include="System.Drawing" /> | |||
<Reference Include="System.Windows.Forms" /> | |||
<Reference Include="System.Xml" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<Compile Include="Program.cs" /> | |||
<Compile Include="Properties\AssemblyInfo.cs" /> | |||
<Compile Include="Resources.Designer.cs"> | |||
<AutoGen>True</AutoGen> | |||
<DesignTime>True</DesignTime> | |||
<DependentUpon>Resources.resx</DependentUpon> | |||
</Compile> | |||
<EmbeddedResource Include="Properties\Resources.resx"> | |||
<Generator>ResXFileCodeGenerator</Generator> | |||
<LastGenOutput>Resources.Designer.cs</LastGenOutput> | |||
<SubType>Designer</SubType> | |||
</EmbeddedResource> | |||
<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="Properties\Settings.settings"> | |||
<Generator>SettingsSingleFileGenerator</Generator> | |||
<LastGenOutput>Settings.Designer.cs</LastGenOutput> | |||
</None> | |||
<Compile Include="Properties\Settings.Designer.cs"> | |||
<AutoGen>True</AutoGen> | |||
<DependentUpon>Settings.settings</DependentUpon> | |||
<DesignTimeSharedInput>True</DesignTimeSharedInput> | |||
</Compile> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<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. | |||
Other similar extension points exist, see Microsoft.Common.targets. | |||
<Target Name="BeforeBuild"> | |||
</Target> | |||
<Target Name="AfterBuild"> | |||
</Target> | |||
--> | |||
</Project> |
@ -0,0 +1,100 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text.RegularExpressions; | |||
using System.Windows.Forms; | |||
namespace Launcher | |||
{ | |||
static class Program | |||
{ | |||
private static string[] TABOO_NAMES = { | |||
//"Start", | |||
//"Update", | |||
//"Awake", | |||
//"OnDestroy" | |||
}; | |||
private static string[] ENTRY_TYPES = { "Display" }; | |||
/// <summary> | |||
/// The main entry point for the application. | |||
/// </summary> | |||
[STAThread] | |||
static void Main() | |||
{ | |||
try { | |||
var execPath = Application.ExecutablePath; | |||
var fileName = Path.GetFileNameWithoutExtension(execPath); | |||
if (fileName.IndexOf("VR") == -1 && fileName.IndexOf("_") == -1) | |||
{ | |||
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"); | |||
EnsureIPA(executable); | |||
StartGame(executable, args.ToArray()); | |||
} | |||
else | |||
{ | |||
MessageBox.Show("Could not find: " + file.FullName, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); | |||
} | |||
} catch(Exception globalException) { | |||
MessageBox.Show(globalException.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); | |||
} | |||
} | |||
private static void EnsureIPA(string executable) | |||
{ | |||
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) | |||
{ | |||
Fail(process.StandardError.ReadToEnd()); | |||
} | |||
} | |||
private static void StartGame(string executable, string[] args) | |||
{ | |||
var arguments = string.Join(" ", args.ToArray()); | |||
Process.Start(executable, arguments); | |||
} | |||
private static void Fail(string reason) { | |||
throw new Exception(reason); | |||
} | |||
/// <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) | |||
{ | |||
if (string.IsNullOrEmpty(original)) | |||
return original; | |||
string value = Regex.Replace(original, @"(\\*)" + "\"", @"$1\$0"); | |||
value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\""); | |||
return value; | |||
} | |||
} | |||
} |
@ -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("Launcher")] | |||
[assembly: AssemblyDescription("Rename to [EXE]_Patched.exe or [EXE]VR.exe")] | |||
[assembly: AssemblyConfiguration("")] | |||
[assembly: AssemblyCompany("")] | |||
[assembly: AssemblyProduct("Launcher")] | |||
[assembly: AssemblyCopyright("Copyright © 2015")] | |||
[assembly: AssemblyTrademark("")] | |||
[assembly: AssemblyCulture("")] | |||
// Setting ComVisible to false makes the types in this assembly not visible | |||
// to COM components. If you need to access a type in this assembly from | |||
// COM, set the ComVisible attribute to true on that type. | |||
[assembly: ComVisible(false)] | |||
// The following GUID is for the ID of the typelib if this project is exposed to COM | |||
[assembly: Guid("d590f676-c2c0-4b80-ae93-6edf274320e6")] | |||
// Version information for an assembly consists of the following four values: | |||
// | |||
// Major Version | |||
// Minor Version | |||
// Build Number | |||
// Revision | |||
// | |||
// You can specify all the values or you can default the Build and Revision Numbers | |||
// by using the '*' as shown below: | |||
// [assembly: AssemblyVersion("1.0.*")] | |||
[assembly: AssemblyVersion("1.0.0.0")] | |||
[assembly: AssemblyFileVersion("1.0.0.0")] |
@ -0,0 +1,63 @@ | |||
//------------------------------------------------------------------------------ | |||
// <auto-generated> | |||
// This code was generated by a tool. | |||
// 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 Launcher.Properties { | |||
using System; | |||
/// <summary> | |||
/// A strongly-typed resource class, for looking up localized strings, etc. | |||
/// </summary> | |||
// This class was auto-generated by the StronglyTypedResourceBuilder | |||
// class via a tool like ResGen or Visual Studio. | |||
// To add or remove a member, edit your .ResX file then rerun ResGen | |||
// with the /str option, or rebuild your VS project. | |||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] | |||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] | |||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] | |||
internal class Resources { | |||
private static global::System.Resources.ResourceManager resourceMan; | |||
private static global::System.Globalization.CultureInfo resourceCulture; | |||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] | |||
internal Resources() { | |||
} | |||
/// <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 (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 { | |||
return resourceCulture; | |||
} | |||
set { | |||
resourceCulture = value; | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,117 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<root> | |||
<!-- | |||
Microsoft ResX Schema | |||
Version 2.0 | |||
The primary goals of this format is to allow a simple XML format | |||
that is mostly human readable. The generation and parsing of the | |||
various data types are done through the TypeConverter classes | |||
associated with the data types. | |||
Example: | |||
... ado.net/XML headers & schema ... | |||
<resheader name="resmimetype">text/microsoft-resx</resheader> | |||
<resheader name="version">2.0</resheader> | |||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> | |||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> | |||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> | |||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> | |||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> | |||
<value>[base64 mime encoded serialized .NET Framework object]</value> | |||
</data> | |||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> | |||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> | |||
<comment>This is a comment</comment> | |||
</data> | |||
There are any number of "resheader" rows that contain simple | |||
name/value pairs. | |||
Each data row contains a name, and value. The row also contains a | |||
type or mimetype. Type corresponds to a .NET class that support | |||
text/value conversion through the TypeConverter architecture. | |||
Classes that don't support this are serialized and stored with the | |||
mimetype set. | |||
The mimetype is used for serialized objects, and tells the | |||
ResXResourceReader how to depersist the object. This is currently not | |||
extensible. For a given mimetype the value must be set accordingly: | |||
Note - application/x-microsoft.net.object.binary.base64 is the format | |||
that the ResXResourceWriter will generate, however the reader can | |||
read any of the formats listed below. | |||
mimetype: application/x-microsoft.net.object.binary.base64 | |||
value : The object must be serialized with | |||
: System.Serialization.Formatters.Binary.BinaryFormatter | |||
: and then encoded with base64 encoding. | |||
mimetype: application/x-microsoft.net.object.soap.base64 | |||
value : The object must be serialized with | |||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter | |||
: and then encoded with base64 encoding. | |||
mimetype: application/x-microsoft.net.object.bytearray.base64 | |||
value : The object must be serialized into a byte array | |||
: using a System.ComponentModel.TypeConverter | |||
: and then encoded with base64 encoding. | |||
--> | |||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> | |||
<xsd:element name="root" msdata:IsDataSet="true"> | |||
<xsd:complexType> | |||
<xsd:choice maxOccurs="unbounded"> | |||
<xsd:element name="metadata"> | |||
<xsd:complexType> | |||
<xsd:sequence> | |||
<xsd:element name="value" type="xsd:string" minOccurs="0" /> | |||
</xsd:sequence> | |||
<xsd:attribute name="name" type="xsd:string" /> | |||
<xsd:attribute name="type" type="xsd:string" /> | |||
<xsd:attribute name="mimetype" type="xsd:string" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
<xsd:element name="assembly"> | |||
<xsd:complexType> | |||
<xsd:attribute name="alias" type="xsd:string" /> | |||
<xsd:attribute name="name" type="xsd:string" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
<xsd:element name="data"> | |||
<xsd:complexType> | |||
<xsd:sequence> | |||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> | |||
</xsd:sequence> | |||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" /> | |||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> | |||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
<xsd:element name="resheader"> | |||
<xsd:complexType> | |||
<xsd:sequence> | |||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||
</xsd:sequence> | |||
<xsd:attribute name="name" type="xsd:string" use="required" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
</xsd:choice> | |||
</xsd:complexType> | |||
</xsd:element> | |||
</xsd:schema> | |||
<resheader name="resmimetype"> | |||
<value>text/microsoft-resx</value> | |||
</resheader> | |||
<resheader name="version"> | |||
<value>2.0</value> | |||
</resheader> | |||
<resheader name="reader"> | |||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</resheader> | |||
<resheader name="writer"> | |||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</resheader> | |||
</root> |
@ -0,0 +1,26 @@ | |||
//------------------------------------------------------------------------------ | |||
// <auto-generated> | |||
// This code was generated by a tool. | |||
// 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 Launcher.Properties { | |||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] | |||
[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 { | |||
return defaultInstance; | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,7 @@ | |||
<?xml version='1.0' encoding='utf-8'?> | |||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)"> | |||
<Profiles> | |||
<Profile Name="(Default)" /> | |||
</Profiles> | |||
<Settings /> | |||
</SettingsFile> |
@ -0,0 +1,63 @@ | |||
//------------------------------------------------------------------------------ | |||
// <auto-generated> | |||
// This code was generated by a tool. | |||
// 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 Launcher { | |||
using System; | |||
/// <summary> | |||
/// A strongly-typed resource class, for looking up localized strings, etc. | |||
/// </summary> | |||
// This class was auto-generated by the StronglyTypedResourceBuilder | |||
// class via a tool like ResGen or Visual Studio. | |||
// To add or remove a member, edit your .ResX file then rerun ResGen | |||
// with the /str option, or rebuild your VS project. | |||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] | |||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] | |||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] | |||
internal class Resources { | |||
private static global::System.Resources.ResourceManager resourceMan; | |||
private static global::System.Globalization.CultureInfo resourceCulture; | |||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] | |||
internal Resources() { | |||
} | |||
/// <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 (object.ReferenceEquals(resourceMan, null)) { | |||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Launcher.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 { | |||
return resourceCulture; | |||
} | |||
set { | |||
resourceCulture = value; | |||
} | |||
} | |||
} | |||
} |
@ -0,0 +1,121 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<root> | |||
<!-- | |||
Microsoft ResX Schema | |||
Version 2.0 | |||
The primary goals of this format is to allow a simple XML format | |||
that is mostly human readable. The generation and parsing of the | |||
various data types are done through the TypeConverter classes | |||
associated with the data types. | |||
Example: | |||
... ado.net/XML headers & schema ... | |||
<resheader name="resmimetype">text/microsoft-resx</resheader> | |||
<resheader name="version">2.0</resheader> | |||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> | |||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> | |||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> | |||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> | |||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> | |||
<value>[base64 mime encoded serialized .NET Framework object]</value> | |||
</data> | |||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> | |||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> | |||
<comment>This is a comment</comment> | |||
</data> | |||
There are any number of "resheader" rows that contain simple | |||
name/value pairs. | |||
Each data row contains a name, and value. The row also contains a | |||
type or mimetype. Type corresponds to a .NET class that support | |||
text/value conversion through the TypeConverter architecture. | |||
Classes that don't support this are serialized and stored with the | |||
mimetype set. | |||
The mimetype is used for serialized objects, and tells the | |||
ResXResourceReader how to depersist the object. This is currently not | |||
extensible. For a given mimetype the value must be set accordingly: | |||
Note - application/x-microsoft.net.object.binary.base64 is the format | |||
that the ResXResourceWriter will generate, however the reader can | |||
read any of the formats listed below. | |||
mimetype: application/x-microsoft.net.object.binary.base64 | |||
value : The object must be serialized with | |||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter | |||
: and then encoded with base64 encoding. | |||
mimetype: application/x-microsoft.net.object.soap.base64 | |||
value : The object must be serialized with | |||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter | |||
: and then encoded with base64 encoding. | |||
mimetype: application/x-microsoft.net.object.bytearray.base64 | |||
value : The object must be serialized into a byte array | |||
: using a System.ComponentModel.TypeConverter | |||
: and then encoded with base64 encoding. | |||
--> | |||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> | |||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> | |||
<xsd:element name="root" msdata:IsDataSet="true"> | |||
<xsd:complexType> | |||
<xsd:choice maxOccurs="unbounded"> | |||
<xsd:element name="metadata"> | |||
<xsd:complexType> | |||
<xsd:sequence> | |||
<xsd:element name="value" type="xsd:string" minOccurs="0" /> | |||
</xsd:sequence> | |||
<xsd:attribute name="name" use="required" type="xsd:string" /> | |||
<xsd:attribute name="type" type="xsd:string" /> | |||
<xsd:attribute name="mimetype" type="xsd:string" /> | |||
<xsd:attribute ref="xml:space" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
<xsd:element name="assembly"> | |||
<xsd:complexType> | |||
<xsd:attribute name="alias" type="xsd:string" /> | |||
<xsd:attribute name="name" type="xsd:string" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
<xsd:element name="data"> | |||
<xsd:complexType> | |||
<xsd:sequence> | |||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> | |||
</xsd:sequence> | |||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> | |||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> | |||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> | |||
<xsd:attribute ref="xml:space" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
<xsd:element name="resheader"> | |||
<xsd:complexType> | |||
<xsd:sequence> | |||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> | |||
</xsd:sequence> | |||
<xsd:attribute name="name" type="xsd:string" use="required" /> | |||
</xsd:complexType> | |||
</xsd:element> | |||
</xsd:choice> | |||
</xsd:complexType> | |||
</xsd:element> | |||
</xsd:schema> | |||
<resheader name="resmimetype"> | |||
<value>text/microsoft-resx</value> | |||
</resheader> | |||
<resheader name="version"> | |||
<value>2.0</value> | |||
</resheader> | |||
<resheader name="reader"> | |||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</resheader> | |||
<resheader name="writer"> | |||
<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" /> | |||
</root> |
@ -0,0 +1 @@ | |||
7c6dbd91916854c83d27e1fd6e1b9ccedbe983b8 |
@ -0,0 +1,4 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<packages> | |||
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net35" /> | |||
</packages> |