Implement assembly patching in installer

This commit is contained in:
Bepis committed 2019-03-11 15:53:29 +11:00
1 parent 98aa08b853
commit a6cb4daff4
11 files changed
+221 -390

No files matched your search

@@ -68,6 +68,7 @@
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TargetGame.cs" />
<Compile Include="Utility.cs" />
<EmbeddedResource Include="formMain.resx">
<DependentUpon>formMain.cs</DependentUpon>
</EmbeddedResource>
+11
View File
@@ -21,8 +21,19 @@ dump-assemblies=false";
public string OutputConfig { get; protected set; }
public string TargetAssembly { get; protected set; }
public string TargetType { get; protected set; }
public string TargetMethod { get; protected set; }
public RuntimePatchesType RuntimePatchesType { get; protected set; }
public Config(string targetAssembly, string targetType, string targetMethod, RuntimePatchesType runtimePatchesType)
{
TargetAssembly = targetAssembly;
TargetType = targetType;
TargetMethod = targetMethod;
RuntimePatchesType = runtimePatchesType;
OutputConfig = ConfigTemplate
.Replace("entrypoint-assembly=UnityEngine.dll", $"entrypoint-assembly={targetAssembly}")
.Replace("entrypoint-type=Application", $"entrypoint-type={targetType}")
+178 -23
View File
@@ -1,7 +1,10 @@
using System;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using Ionic.Zip;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace BepInEx.Installer.Patching
{
@@ -22,17 +25,27 @@ namespace BepInEx.Installer.Patching
public void Install()
{
switch (InstallationType)
try
{
case InstallationType.Doorstop:
DoorstopInstallation();
break;
case InstallationType.CryptoRng:
throw new NotImplementedException();
case InstallationType.AssemblyPatch:
break;
default:
throw new ArgumentOutOfRangeException(nameof(InstallationType));
switch (InstallationType)
{
case InstallationType.Doorstop:
DoorstopInstallation();
break;
case InstallationType.CryptoRng:
throw new NotImplementedException();
case InstallationType.AssemblyPatch:
AssemblyPatchInstallation();
break;
default:
throw new ArgumentOutOfRangeException(nameof(InstallationType));
}
MessageBox.Show("Installation completed successfully.", "Installation", MessageBoxButtons.OK, MessageBoxIcon.None);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error during installation", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
@@ -41,16 +54,7 @@ namespace BepInEx.Installer.Patching
return ZipFile.Read(EmbeddedResource.GetStream("BepInEx.Installer.InstallPackage.zip"));
}
private static MemoryStream ExtractZipEntry(ZipEntry entry)
{
MemoryStream ms = new MemoryStream((int)entry.UncompressedSize);
entry.Extract(ms);
ms.Position = 0;
return ms;
}
#region Doorstop
private const string DoorstopConfig = @"[UnityDoorstop]
# Specifies whether assembly executing is enabled
@@ -75,7 +79,7 @@ targetAssembly=BepInEx\core\BepInEx.Preloader.dll";
using (var packageZip = GetPackageZip())
{
using (var doorstopZip = ZipFile.Read(ExtractZipEntry(packageZip[doorstopZipName])))
using (var doorstopZip = ZipFile.Read(packageZip[doorstopZipName].ExtractEntry()))
{
doorstopZip["winhttp.dll"].Extract(TargetGame.DirectoryPath, ExtractExistingFileAction.OverwriteSilently);
}
@@ -87,8 +91,7 @@ targetAssembly=BepInEx\core\BepInEx.Preloader.dll";
{
string localFilename = Path.Combine(coreDirectory, Path.GetFileName(entry.FileName));
using (FileStream fs = new FileStream(localFilename, FileMode.Create))
entry.Extract(fs);
entry.ExtractEntry(localFilename);
}
}
@@ -96,5 +99,157 @@ targetAssembly=BepInEx\core\BepInEx.Preloader.dll";
File.WriteAllText(Path.Combine(TargetGame.DirectoryPath, "doorstop_config.ini"), DoorstopConfig);
}
#endregion
#region Assembly Patch
private void AssemblyPatchInstallation()
{
string bepinexDirectory = Path.Combine(TargetGame.DirectoryPath, "BepInEx");
string coreDirectory = Path.Combine(bepinexDirectory, "core");
string managedDir = Path.Combine(TargetGame.DataFolder, "Managed");
string unityEngineDllPath = Path.Combine(managedDir, Config.TargetAssembly);
string bootstrapDllPath = Path.Combine(managedDir, "BepInEx.Bootstrap.dll");
if (!File.Exists(unityEngineDllPath))
throw new ArgumentException($"Cannot find target assembly '{unityEngineDllPath}'");
Directory.CreateDirectory(coreDirectory);
string coreAssemblyZipFolder = TargetGame.UnityEngineType == TargetGame.UnityGameType.v2017Plus
? "v2018"
: "legacy";
using (var packageZip = GetPackageZip())
{
var selectedEntries = packageZip.SelectEntries("*", "shared")
.Concat(packageZip.SelectEntries("*", coreAssemblyZipFolder))
.Where(x => !x.FileName.EndsWith("BepInEx.Preloader.dll"));
foreach (var entry in selectedEntries)
{
string localFilename = Path.Combine(coreDirectory, Path.GetFileName(entry.FileName));
entry.ExtractEntry(localFilename);
}
packageZip["bootstrap\\BepInEx.Bootstrap.dll"].ExtractEntry(bootstrapDllPath);
}
if (!PatchUnityGame(managedDir, unityEngineDllPath, bootstrapDllPath, out var message))
throw new Exception(message);
}
bool PatchUnityGame(string managedDir, string unityEngineDll, string bootstrapDll, out string message)
{
var defaultResolver = new DefaultAssemblyResolver();
defaultResolver.AddSearchDirectory(managedDir);
var rp = new ReaderParameters
{
AssemblyResolver = defaultResolver
};
string unityBackupDll = Path.GetFullPath($"{unityEngineDll}.bak");
//determine which assembly to use as a base
AssemblyDefinition unity = AssemblyDefinition.ReadAssembly(unityEngineDll, rp);
if (!VerifyAssembly(unity))
{
//try and fall back to .bak if exists
if (File.Exists(unityBackupDll))
{
unity.Dispose();
unity = AssemblyDefinition.ReadAssembly(unityBackupDll, rp);
if (!VerifyAssembly(unity))
{
//can't use anything
unity.Dispose();
message = "Target assembly already has BepInEx injected, and the backup is not usable.";
return false;
}
}
else
{
//can't use anything
unity.Dispose();
message = "Target assembly already has BepInEx injected, and no backup exists.";
return false;
}
}
else
{
//make a backup of the assembly
File.Copy(unityEngineDll, unityBackupDll, true);
unity.Dispose();
unity = AssemblyDefinition.ReadAssembly(unityBackupDll, rp);
}
//patch
using (unity)
using (AssemblyDefinition injected = AssemblyDefinition.ReadAssembly(bootstrapDll, rp))
{
InjectAssembly(unity, injected);
unity.Write(unityEngineDll);
}
message = null;
return true;
}
void InjectAssembly(AssemblyDefinition targetAssembly, AssemblyDefinition injected)
{
//Entry point
var originalInjectMethod = injected.MainModule.Types.First(x => x.Name == "Entrypoint")
.Methods.First(x => x.Name == "Init");
var injectMethod = targetAssembly.MainModule.ImportReference(originalInjectMethod);
var targetType = targetAssembly.MainModule.Types.FirstOrDefault(x => x.Name == Config.TargetType);
if (targetType == null)
throw new ArgumentException($"Cannot find target type '{Config.TargetType}'");
MethodDefinition targetMethod;
if (Config.TargetMethod == ".cctor")
{
targetMethod = targetType.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic);
if (targetMethod == null)
{
targetMethod = new MethodDefinition(".cctor",
MethodAttributes.Static
| MethodAttributes.Private
| MethodAttributes.HideBySig
| MethodAttributes.SpecialName
| MethodAttributes.RTSpecialName,
targetAssembly.MainModule.ImportReference(typeof(void)));
targetType.Methods.Add(targetMethod);
var il = targetMethod.Body.GetILProcessor();
il.Append(il.Create(OpCodes.Ret));
}
}
else
{
targetMethod = targetType.Methods.FirstOrDefault(m => m.Name == Config.TargetMethod);
if (targetMethod == null)
throw new ArgumentException($"Cannot find target method '{Config.TargetType}.{Config.TargetMethod}'");
}
var ilp = targetMethod.Body.GetILProcessor();
ilp.InsertBefore(ilp.Body.Instructions.First(), ilp.Create(OpCodes.Call, injectMethod));
}
static bool VerifyAssembly(AssemblyDefinition unity)
=> !unity.MainModule.AssemblyReferences.Any(x => x.Name.Contains("BepInEx"));
#endregion
}
}
+29
View File
@@ -0,0 +1,29 @@
using System.IO;
using System.Linq;
using Ionic.Zip;
namespace BepInEx.Installer
{
internal static class Utility
{
public static string CombinePaths(params string[] pathSegments)
=> pathSegments.Aggregate(Path.Combine);
public static MemoryStream ExtractEntry(this ZipEntry entry)
{
MemoryStream ms = new MemoryStream((int)entry.UncompressedSize);
entry.Extract(ms);
ms.Position = 0;
return ms;
}
public static void ExtractEntry(this ZipEntry entry, string filename)
{
using (FileStream fs = new FileStream(filename, FileMode.Create))
entry.Extract(fs);
}
}
}
-83
View File
@@ -1,83 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\ILRepack.2.0.15\build\ILRepack.props" Condition="Exists('..\packages\ILRepack.2.0.15\build\ILRepack.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>{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>BepInEx.Patcher</RootNamespace>
<AssemblyName>BepInEx.Patcher</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup>
<StartupObject>BepInEx.Patcher.Program</StartupObject>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Legacy|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\bin\patcher\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>embedded</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'v2018|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\bin\patcher\</OutputPath>
<DefineConstants>TRACE;UNITY_2018</DefineConstants>
<Optimize>true</Optimize>
<DebugType>embedded</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Cecil, Version=0.10.0.0, Culture=neutral, PublicKeyToken=50cebf1cceb9d05e, processorArchitecture=MSIL">
<HintPath>..\packages\Mono.Cecil.0.10.0\lib\net35\Mono.Cecil.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="EmbeddedResource.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<EmbeddedResource Include="..\bin\patcher\BepInEx.Bootstrap.dll" />
</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\ILRepack.2.0.15\build\ILRepack.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ILRepack.2.0.15\build\ILRepack.props'))" />
<Error Condition="!Exists('..\packages\ILRepack.MSBuild.Task.1.0.9\build\ILRepack.MSBuild.Task.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ILRepack.MSBuild.Task.1.0.9\build\ILRepack.MSBuild.Task.targets'))" />
</Target>
<!-- ILRepack -->
<Target Name="AfterBuild">
<Move SourceFiles="$(OutputPath)\$(AssemblyName).exe" DestinationFiles="$(OutputPath)\$(AssemblyName)1.exe" />
<ItemGroup>
<InputAssemblies Include="$(OutputPath)\$(AssemblyName)1.exe" />
<InputAssemblies Include="$(OutputPath)\Mono.Cecil.dll" />
</ItemGroup>
<ILRepack Parallel="true" Internalize="true" DebugInfo="true" PrimaryAssemblyFile="$(OutputPath)\$(AssemblyName)1.exe" InputAssemblies="@(InputAssemblies)" TargetKind="Exe" TargetPlatformVersion="v2" OutputFile="$(OutputPath)\$(AssemblyName).exe" />
<Delete Files="@(InputAssemblies)" />
<Delete Files="$(OutputPath)\BepInEx.Bootstrap.dll" />
</Target>
<!-- /ILRepack -->
<Import Project="..\packages\ILRepack.MSBuild.Task.1.0.9\build\ILRepack.MSBuild.Task.targets" Condition="Exists('..\packages\ILRepack.MSBuild.Task.1.0.9\build\ILRepack.MSBuild.Task.targets')" />
</Project>
-22
View File
@@ -1,22 +0,0 @@
using System.IO;
using System.Reflection;
namespace BepInEx.Patcher
{
internal static class EmbeddedResource
{
public static byte[] Get(string resourceName)
{
using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
int length = (int)stream.Length;
byte[] buffer = new byte[length];
stream.Read(buffer, 0, length);
return buffer;
}
}
}
}
-206
View File
@@ -1,206 +0,0 @@
using Mono.Cecil;
using Mono.Cecil.Cil;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using MethodAttributes = Mono.Cecil.MethodAttributes;
namespace BepInEx.Patcher
{
class Program
{
static void WriteError(string message)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Failed");
Console.ResetColor();
Console.WriteLine(message);
Console.WriteLine();
}
static void WriteSuccess()
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Success");
Console.ResetColor();
}
static void Main(string[] args)
{
Console.WriteLine($"BepInEx Patcher v{Assembly.GetExecutingAssembly().GetName().Version}");
if (args.Length >= 1) //short circuit for specific dll patch
Environment.Exit(PatchUnityExe(Path.GetDirectoryName(args[0]), args[0], out string message) ? 0 : 9999);
bool hasFound = false;
bool hasFailure = false;
int patchCount = 0;
foreach (string exePath in Directory.GetFiles(Directory.GetCurrentDirectory()))
{
string gameName = Path.GetFileNameWithoutExtension(exePath);
string managedDir = Environment.CurrentDirectory + $@"\{gameName}_Data\Managed";
#if UNITY_2018
string unityOutputDLL = Path.GetFullPath($"{managedDir}\\UnityEngine.CoreModule.dll");
#else
string unityOutputDLL = Path.GetFullPath($"{managedDir}\\UnityEngine.dll");
#endif
if (!Directory.Exists(managedDir) || !File.Exists(unityOutputDLL))
continue;
hasFound = true;
Console.Write($"Patching {gameName}... ");
if (PatchUnityExe(managedDir, unityOutputDLL, out string message))
{
WriteSuccess();
patchCount++;
}
else
{
WriteError(message);
hasFailure = true;
}
}
Console.WriteLine();
if (!hasFound)
Console.WriteLine("Didn't find any games to patch! Exiting.");
else
Console.WriteLine($"Patched {patchCount} assemblies!");
if (hasFailure)
{
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
else
System.Threading.Thread.Sleep(3000);
}
static bool PatchUnityExe(string managedDir, string unityOutputDLL, out string message)
{
message = null;
try
{
string injectedDLL = Path.GetFullPath($"{managedDir}\\BepInEx.Bootstrap.dll");
File.WriteAllBytes(injectedDLL, EmbeddedResource.Get("BepInEx.Patcher.BepInEx.Bootstrap.dll"));
var defaultResolver = new DefaultAssemblyResolver();
defaultResolver.AddSearchDirectory(managedDir);
var rp = new ReaderParameters
{
AssemblyResolver = defaultResolver
};
#if UNITY_2018
string unityBackupDLL = Path.GetFullPath($"{managedDir}\\UnityEngine.CoreModule.dll.bak");
#else
string unityBackupDLL = Path.GetFullPath($"{managedDir}\\UnityEngine.dll.bak");
#endif
//determine which assembly to use as a base
AssemblyDefinition unity = AssemblyDefinition.ReadAssembly(unityOutputDLL, rp);
if (!VerifyAssembly(unity, out message))
{
//try and fall back to .bak if exists
if (File.Exists(unityBackupDLL))
{
unity.Dispose();
unity = AssemblyDefinition.ReadAssembly(unityBackupDLL, rp);
if (!VerifyAssembly(unity, out message))
{
//can't use anything
unity.Dispose();
message += "\r\nThe backup is not usable.";
return false;
}
}
else
{
//can't use anything
unity.Dispose();
message += "\r\nNo backup exists.";
return false;
}
}
else
{
//make a backup of the assembly
File.Copy(unityOutputDLL, unityBackupDLL, true);
unity.Dispose();
unity = AssemblyDefinition.ReadAssembly(unityBackupDLL, rp);
}
//patch
using (unity)
using (AssemblyDefinition injected = AssemblyDefinition.ReadAssembly(injectedDLL, rp))
{
InjectAssembly(unity, injected);
unity.Write(unityOutputDLL);
}
}
catch (Exception e)
{
message = e.ToString();
return false;
}
return true;
}
static void InjectAssembly(AssemblyDefinition unity, AssemblyDefinition injected)
{
//Entry point
var originalInjectMethod = injected.MainModule.Types.First(x => x.Name == "Entrypoint")
.Methods.First(x => x.Name == "Init");
var injectMethod = unity.MainModule.ImportReference(originalInjectMethod);
var sceneManager = unity.MainModule.Types.First(x => x.Name == "Application");
var voidType = unity.MainModule.ImportReference(typeof(void));
var cctor = new MethodDefinition(".cctor",
MethodAttributes.Static
| MethodAttributes.Private
| MethodAttributes.HideBySig
| MethodAttributes.SpecialName
| MethodAttributes.RTSpecialName,
voidType);
var ilp = cctor.Body.GetILProcessor();
ilp.Append(ilp.Create(OpCodes.Call, injectMethod));
ilp.Append(ilp.Create(OpCodes.Ret));
sceneManager.Methods.Add(cctor);
}
static bool VerifyAssembly(AssemblyDefinition unity, out string message)
{
bool canPatch = true;
message = "";
//check if already patched
if (unity.MainModule.AssemblyReferences.Any(x => x.Name.Contains("BepInEx")))
{
canPatch = false;
message += "This assembly has already been patched by BepInEx.\r\n";
}
message = message.Trim();
return canPatch;
}
}
}
@@ -1,36 +0,0 @@
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("BepInEx.Patcher")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BepInEx.Patcher")]
[assembly: AssemblyCopyright("Copyright © Bepis 2018")]
[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("dc89f18b-235b-4c01-ab31-af40dce5c4c7")]
// 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("5.0.0.0")]
[assembly: AssemblyFileVersion("5.0.0.0")]
-7
View File
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ILRepack" version="2.0.15" targetFramework="net35" />
<package id="ILRepack.MSBuild.Task" version="1.0.9" targetFramework="net35" />
<package id="Mono.Cecil" version="0.10.0" targetFramework="net35" />
</packages>
-10
View File
@@ -5,12 +5,6 @@ VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepInEx", "BepInEx\BepInEx.csproj", "{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepInEx.Patcher", "BepInEx.Patcher\BepInEx.Patcher.csproj", "{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}"
ProjectSection(ProjectDependencies) = postProject
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9} = {4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB} = {6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BepInEx.Bootstrap", "BepInEx.Bootstrap\BepInEx.Bootstrap.csproj", "{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Submodules", "Submodules", "{BAC58F7E-AAD8-4D0C-9490-9765ACBBA6FB}"
@@ -37,9 +31,6 @@ Global
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.Legacy|Any CPU.Build.0 = Legacy|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Any CPU.ActiveCfg = v2018|Any CPU
{4FFBA620-F5ED-47F9-B90C-DAD1316FD9B9}.v2018|Any CPU.Build.0 = v2018|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.InstallerDebug|Any CPU.ActiveCfg = Debug|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.Legacy|Any CPU.ActiveCfg = Legacy|Any CPU
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7}.v2018|Any CPU.ActiveCfg = v2018|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.InstallerDebug|Any CPU.ActiveCfg = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Any CPU.ActiveCfg = Release|Any CPU
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB}.Legacy|Any CPU.Build.0 = Release|Any CPU
@@ -69,7 +60,6 @@ Global
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{DC89F18B-235B-4C01-AB31-AF40DCE5C4C7} = {A9071994-3533-4C1B-89DC-D817B676AB41}
{6E6BC1E5-5BE8-4566-B3AE-52C4CB218AEB} = {A9071994-3533-4C1B-89DC-D817B676AB41}
{54161CFE-FF42-4DDE-B161-3A49545DB5CD} = {BAC58F7E-AAD8-4D0C-9490-9765ACBBA6FB}
{A15D6EE6-F954-415B-8605-8A8470CC87DC} = {BAC58F7E-AAD8-4D0C-9490-9765ACBBA6FB}
+2 -3
View File
@@ -14,19 +14,18 @@ New-Item $tempPath -ItemType Directory -Force | Out-Null
#download doorstop
$doorstopTag = "v2.7.1.0"
$doorstopVersion = "2.7.1.0"
Write-Output "Downloading Doorstop $doorstopTag"
$wc = New-Object System.Net.WebClient
$url = "https://github.com/NeighTools/UnityDoorstop/releases/download/$doorstopTag/Doorstop_x64_$doorstopVersion.zip"
$url = "https://github.com/NeighTools/UnityDoorstop/releases/download/v$doorstopVersion/Doorstop_x64_$doorstopVersion.zip"
$output = "$tempPath\doorstop_x64.zip"
$wc.DownloadFile($url, $output)
$url = "https://github.com/NeighTools/UnityDoorstop/releases/download/$doorstopTag/Doorstop_x86_$doorstopVersion.zip"
$url = "https://github.com/NeighTools/UnityDoorstop/releases/download/v$doorstopVersion/Doorstop_x86_$doorstopVersion.zip"
$output = "$tempPath\doorstop_x86.zip"
$wc.DownloadFile($url, $output)