From a6cb4daff46ca5d597a6a520b6c917818403cc7b Mon Sep 17 00:00:00 2001 From: Bepis <36346617+bbepis@users.noreply.github.com> Date: Mon, 11 Mar 2019 15:53:29 +1100 Subject: [PATCH] Implement assembly patching in installer --- BepInEx.Installer/BepInEx.Installer.csproj | 1 + BepInEx.Installer/Config.cs | 11 + .../Patching/FrameworkInstaller.cs | 201 +++++++++++++++-- BepInEx.Installer/Utility.cs | 29 +++ BepInEx.Patcher/BepInEx.Patcher.csproj | 83 ------- BepInEx.Patcher/EmbeddedResource.cs | 22 -- BepInEx.Patcher/Program.cs | 206 ------------------ BepInEx.Patcher/Properties/AssemblyInfo.cs | 36 --- BepInEx.Patcher/packages.config | 7 - BepInEx.sln | 10 - scripts/createInstallerPackage.ps1 | 5 +- 11 files changed, 221 insertions(+), 390 deletions(-) create mode 100644 BepInEx.Installer/Utility.cs delete mode 100644 BepInEx.Patcher/BepInEx.Patcher.csproj delete mode 100644 BepInEx.Patcher/EmbeddedResource.cs delete mode 100644 BepInEx.Patcher/Program.cs delete mode 100644 BepInEx.Patcher/Properties/AssemblyInfo.cs delete mode 100644 BepInEx.Patcher/packages.config diff --git a/BepInEx.Installer/BepInEx.Installer.csproj b/BepInEx.Installer/BepInEx.Installer.csproj index e32323d..fc9bc00 100644 --- a/BepInEx.Installer/BepInEx.Installer.csproj +++ b/BepInEx.Installer/BepInEx.Installer.csproj @@ -68,6 +68,7 @@ + formMain.cs diff --git a/BepInEx.Installer/Config.cs b/BepInEx.Installer/Config.cs index 31882fe..4d9d31d 100644 --- a/BepInEx.Installer/Config.cs +++ b/BepInEx.Installer/Config.cs @@ -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}") diff --git a/BepInEx.Installer/Patching/FrameworkInstaller.cs b/BepInEx.Installer/Patching/FrameworkInstaller.cs index 7c6d8ea..7693da6 100644 --- a/BepInEx.Installer/Patching/FrameworkInstaller.cs +++ b/BepInEx.Installer/Patching/FrameworkInstaller.cs @@ -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 } } \ No newline at end of file diff --git a/BepInEx.Installer/Utility.cs b/BepInEx.Installer/Utility.cs new file mode 100644 index 0000000..3c39fba --- /dev/null +++ b/BepInEx.Installer/Utility.cs @@ -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); + } + } +} diff --git a/BepInEx.Patcher/BepInEx.Patcher.csproj b/BepInEx.Patcher/BepInEx.Patcher.csproj deleted file mode 100644 index d638d34..0000000 --- a/BepInEx.Patcher/BepInEx.Patcher.csproj +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - Debug - AnyCPU - {DC89F18B-235B-4C01-AB31-AF40DCE5C4C7} - Exe - BepInEx.Patcher - BepInEx.Patcher - v3.5 - 512 - true - - - - - - BepInEx.Patcher.Program - - - true - ..\bin\patcher\ - TRACE - true - embedded - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - true - ..\bin\patcher\ - TRACE;UNITY_2018 - true - embedded - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - - ..\packages\Mono.Cecil.0.10.0\lib\net35\Mono.Cecil.dll - True - - - - - - - - - - - - - - - - - - - - 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}. - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/BepInEx.Patcher/EmbeddedResource.cs b/BepInEx.Patcher/EmbeddedResource.cs deleted file mode 100644 index 3c0bda9..0000000 --- a/BepInEx.Patcher/EmbeddedResource.cs +++ /dev/null @@ -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; - } - } - } -} \ No newline at end of file diff --git a/BepInEx.Patcher/Program.cs b/BepInEx.Patcher/Program.cs deleted file mode 100644 index 187168a..0000000 --- a/BepInEx.Patcher/Program.cs +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/BepInEx.Patcher/Properties/AssemblyInfo.cs b/BepInEx.Patcher/Properties/AssemblyInfo.cs deleted file mode 100644 index 1df26b3..0000000 --- a/BepInEx.Patcher/Properties/AssemblyInfo.cs +++ /dev/null @@ -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")] \ No newline at end of file diff --git a/BepInEx.Patcher/packages.config b/BepInEx.Patcher/packages.config deleted file mode 100644 index 90ecfe2..0000000 --- a/BepInEx.Patcher/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/BepInEx.sln b/BepInEx.sln index c928b7d..64033a4 100644 --- a/BepInEx.sln +++ b/BepInEx.sln @@ -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} diff --git a/scripts/createInstallerPackage.ps1 b/scripts/createInstallerPackage.ps1 index fc012bd..428363b 100644 --- a/scripts/createInstallerPackage.ps1 +++ b/scripts/createInstallerPackage.ps1 @@ -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)