Rework Windows launchers:
- Use SDL2 message boxes instead of Winforms. - Use a proper project instead of compiling a single file. - Use assembly attributes instead of modifying strings in the source code. - Remove generic OpenRA.exe launcher. - Replace MakeLAA.exe with a python script.
This commit is contained in:
3
Makefile
3
Makefile
@@ -76,9 +76,6 @@ INSTALL_DATA = $(INSTALL) -m644
|
|||||||
# Toolchain
|
# Toolchain
|
||||||
MSBUILD = msbuild -verbosity:m -nologo
|
MSBUILD = msbuild -verbosity:m -nologo
|
||||||
|
|
||||||
# Enable 32 bit builds while generating the windows installer
|
|
||||||
WIN32 = false
|
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
ifndef TARGETPLATFORM
|
ifndef TARGETPLATFORM
|
||||||
UNAME_S := $(shell uname -s)
|
UNAME_S := $(shell uname -s)
|
||||||
|
|||||||
6
OpenRA.WindowsLauncher/App.config
Normal file
6
OpenRA.WindowsLauncher/App.config
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<runtime>
|
||||||
|
<loadFromRemoteSources enabled="true" />
|
||||||
|
</runtime>
|
||||||
|
</configuration>
|
||||||
61
OpenRA.WindowsLauncher/OpenRA.WindowsLauncher.csproj
Normal file
61
OpenRA.WindowsLauncher/OpenRA.WindowsLauncher.csproj
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>winexe</OutputType>
|
||||||
|
<TargetFramework>net472</TargetFramework>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||||
|
<LangVersion>7.3</LangVersion>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
|
||||||
|
<OutputPath>../bin</OutputPath>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<ExternalConsole>false</ExternalConsole>
|
||||||
|
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||||
|
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<Configurations>Release;Debug</Configurations>
|
||||||
|
<AssemblyName>$(LauncherName)</AssemblyName>
|
||||||
|
<ApplicationIcon>$(LauncherIcon)</ApplicationIcon>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->
|
||||||
|
<Compile Include="**/*.cs" Exclude="$(DefaultItemExcludes)" />
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(TargetPlatform)' == 'win-x86'">
|
||||||
|
<Prefer32bit>true</Prefer32bit>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AdditionalFiles Include="../stylecop.json" />
|
||||||
|
<None Include="App.config" />
|
||||||
|
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute" Condition="$(ModID) != ''">
|
||||||
|
<_Parameter1>ModID</_Parameter1>
|
||||||
|
<_Parameter2>$(ModID)</_Parameter2>
|
||||||
|
</AssemblyAttribute>
|
||||||
|
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute" Condition="$(DisplayName) != ''">
|
||||||
|
<_Parameter1>DisplayName</_Parameter1>
|
||||||
|
<_Parameter2>$(DisplayName)</_Parameter2>
|
||||||
|
</AssemblyAttribute>
|
||||||
|
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute" Condition="$(FaqUrl) != ''">
|
||||||
|
<_Parameter1>FaqUrl</_Parameter1>
|
||||||
|
<_Parameter2>$(FaqUrl)</_Parameter2>
|
||||||
|
</AssemblyAttribute>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="OpenRA-SDL2-CS" Version="1.0.27" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Target Name="DisableAnalyzers" BeforeTargets="CoreCompile" Condition="'$(Configuration)'=='Release'">
|
||||||
|
<!-- Disable code style analysis on Release builds to improve compile-time performance -->
|
||||||
|
<ItemGroup Condition="'$(Configuration)'=='Release'">
|
||||||
|
<Analyzer Remove="@(Analyzer)" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
197
OpenRA.WindowsLauncher/Program.cs
Normal file
197
OpenRA.WindowsLauncher/Program.cs
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
#region Copyright & License Information
|
||||||
|
/*
|
||||||
|
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||||
|
* This file is part of OpenRA, which is free software. It is made
|
||||||
|
* available to you under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, either version 3 of
|
||||||
|
* the License, or (at your option) any later version. For more
|
||||||
|
* information, see COPYING.
|
||||||
|
*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using SDL2;
|
||||||
|
|
||||||
|
namespace OpenRA.WindowsLauncher
|
||||||
|
{
|
||||||
|
class WindowsLauncher
|
||||||
|
{
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
static extern bool SetForegroundWindow(IntPtr hWnd);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
static extern bool AllowSetForegroundWindow(int dwProcessId);
|
||||||
|
|
||||||
|
static Process gameProcess;
|
||||||
|
static string modID;
|
||||||
|
static string displayName;
|
||||||
|
static string faqUrl;
|
||||||
|
|
||||||
|
static int Main(string[] args)
|
||||||
|
{
|
||||||
|
// The modID, displayName, and faqUrl variables are embedded in the assembly metadata by defining
|
||||||
|
// -p:ModID="mymod", -p:DisplayName="My Mod", -p:FaqUrl="https://my.tld/faq" when compiling the project
|
||||||
|
var attributes = Assembly.GetExecutingAssembly().GetCustomAttributes();
|
||||||
|
foreach (var a in attributes)
|
||||||
|
{
|
||||||
|
if (a is AssemblyMetadataAttribute metadata)
|
||||||
|
{
|
||||||
|
switch (metadata.Key)
|
||||||
|
{
|
||||||
|
case "ModID": modID = metadata.Value; break;
|
||||||
|
case "DisplayName": displayName = metadata.Value; break;
|
||||||
|
case "FaqUrl": faqUrl = metadata.Value; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Any(x => x.StartsWith("Engine.LaunchPath=", StringComparison.Ordinal)))
|
||||||
|
return RunGame(args);
|
||||||
|
|
||||||
|
return RunInnerLauncher(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int RunGame(string[] args)
|
||||||
|
{
|
||||||
|
var launcherPath = Assembly.GetExecutingAssembly().Location;
|
||||||
|
var directory = Path.GetDirectoryName(launcherPath);
|
||||||
|
Directory.SetCurrentDirectory(directory);
|
||||||
|
|
||||||
|
AppDomain.CurrentDomain.UnhandledException += (_, e) => ExceptionHandler.HandleFatalError((Exception)e.ExceptionObject);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return (int)Game.InitializeAndRun(args);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// We must grant permission for the launcher process to bring the error dialog to the foreground.
|
||||||
|
// Finding the parent process id is unreasonably difficult on Windows, so instead pass -1 to enable for all processes.
|
||||||
|
AllowSetForegroundWindow(-1);
|
||||||
|
ExceptionHandler.HandleFatalError(e);
|
||||||
|
return (int)RunStatus.Error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int RunInnerLauncher(string[] args)
|
||||||
|
{
|
||||||
|
var launcherPath = Process.GetCurrentProcess().MainModule.FileName;
|
||||||
|
var launcherArgs = args.ToList();
|
||||||
|
|
||||||
|
if (!launcherArgs.Any(x => x.StartsWith("Engine.LaunchPath=", StringComparison.Ordinal)))
|
||||||
|
launcherArgs.Add("Engine.LaunchPath=\"" + launcherPath + "\"");
|
||||||
|
|
||||||
|
if (!launcherArgs.Any(x => x.StartsWith("Game.Mod=", StringComparison.Ordinal)))
|
||||||
|
launcherArgs.Add("Game.Mod=" + modID);
|
||||||
|
|
||||||
|
var psi = new ProcessStartInfo(launcherPath, string.Join(" ", launcherArgs));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
gameProcess = Process.Start(psi);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gameProcess == null)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
gameProcess.EnableRaisingEvents = true;
|
||||||
|
gameProcess.Exited += GameProcessExited;
|
||||||
|
gameProcess.WaitForExit();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ShowErrorDialog()
|
||||||
|
{
|
||||||
|
var viewLogs = new SDL.SDL_MessageBoxButtonData
|
||||||
|
{
|
||||||
|
buttonid = 2,
|
||||||
|
text = "View Logs",
|
||||||
|
flags = SDL.SDL_MessageBoxButtonFlags.SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT
|
||||||
|
};
|
||||||
|
|
||||||
|
var viewFaq = new SDL.SDL_MessageBoxButtonData
|
||||||
|
{
|
||||||
|
buttonid = 1,
|
||||||
|
text = "View FAQ"
|
||||||
|
};
|
||||||
|
|
||||||
|
var quit = new SDL.SDL_MessageBoxButtonData
|
||||||
|
{
|
||||||
|
buttonid = 0,
|
||||||
|
text = "Quit",
|
||||||
|
flags = SDL.SDL_MessageBoxButtonFlags.SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT
|
||||||
|
};
|
||||||
|
|
||||||
|
var dialog = new SDL.SDL_MessageBoxData
|
||||||
|
{
|
||||||
|
flags = SDL.SDL_MessageBoxFlags.SDL_MESSAGEBOX_ERROR,
|
||||||
|
title = "Fatal Error",
|
||||||
|
message = displayName + " has encountered a fatal error and must close.\nRefer to the crash logs and FAQ for more information.",
|
||||||
|
buttons = new[] { quit, viewFaq, viewLogs },
|
||||||
|
numbuttons = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
// SDL_ShowMessageBox may create the error dialog behind other windows.
|
||||||
|
// We want to bring it to the foreground, but can't do it from the main thread
|
||||||
|
// because SDL_ShowMessageBox blocks until the user presses a button.
|
||||||
|
// HACK: Spawn a thread to raise it to the foreground after a short delay.
|
||||||
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
SetForegroundWindow(Process.GetCurrentProcess().MainWindowHandle);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (SDL.SDL_ShowMessageBox(ref dialog, out var buttonid) < 0)
|
||||||
|
Exit();
|
||||||
|
|
||||||
|
switch (buttonid)
|
||||||
|
{
|
||||||
|
case 0: Exit(); break;
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Process.Start(faqUrl);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Process.Start(Path.Combine(Platform.SupportDir, "Logs"));
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GameProcessExited(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (gameProcess.ExitCode != (int)RunStatus.Success)
|
||||||
|
ShowErrorDialog();
|
||||||
|
|
||||||
|
Exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Exit()
|
||||||
|
{
|
||||||
|
Environment.Exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenRA.Test", "OpenRA.Test\
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenRA.Launcher", "OpenRA.Launcher\OpenRA.Launcher.csproj", "{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenRA.Launcher", "OpenRA.Launcher\OpenRA.Launcher.csproj", "{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenRA.WindowsLauncher", "OpenRA.WindowsLauncher\OpenRA.WindowsLauncher.csproj", "{912B578E-0BC0-4987-807B-1E294A87DA37}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -63,6 +65,8 @@ Global
|
|||||||
{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}.Release|Any CPU.Build.0 = Release|Any CPU
|
{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{912B578E-0BC0-4987-807B-1E294A87DA37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{912B578E-0BC0-4987-807B-1E294A87DA37}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -15,13 +15,10 @@ after_test:
|
|||||||
- python -c "from PIL import Image; i = Image.open('packaging/artwork/ra_256x256.png'); i.save('ra.ico')"
|
- python -c "from PIL import Image; i = Image.open('packaging/artwork/ra_256x256.png'); i.save('ra.ico')"
|
||||||
- python -c "from PIL import Image; i = Image.open('packaging/artwork/cnc_256x256.png'); i.save('cnc.ico')"
|
- python -c "from PIL import Image; i = Image.open('packaging/artwork/cnc_256x256.png'); i.save('cnc.ico')"
|
||||||
- python -c "from PIL import Image; i = Image.open('packaging/artwork/d2k_256x256.png'); i.save('d2k.ico')"
|
- python -c "from PIL import Image; i = Image.open('packaging/artwork/d2k_256x256.png'); i.save('d2k.ico')"
|
||||||
|
- msbuild -t:Build "OpenRA.WindowsLauncher/OpenRA.WindowsLauncher.csproj" -restore -p:Configuration=Release -p:TargetPlatform="win-x64" -p:LauncherName="RedAlert" -p:LauncherIcon="../ra.ico" -p:ModID="ra" -p:DisplayName="Red Alert" -p:FaqUrl="http://wiki.openra.net/FAQ"
|
||||||
|
- msbuild -t:Build "OpenRA.WindowsLauncher/OpenRA.WindowsLauncher.csproj" -restore -p:Configuration=Release -p:TargetPlatform="win-x64" -p:LauncherName="TiberianDawn" -p:LauncherIcon="../cnc.ico" -p:ModID="cnc" -p:DisplayName="Tiberian Dawn" -p:FaqUrl="http://wiki.openra.net/FAQ"
|
||||||
|
- msbuild -t:Build "OpenRA.WindowsLauncher/OpenRA.WindowsLauncher.csproj" -restore -p:Configuration=Release -p:TargetPlatform="win-x64" -p:LauncherName="Dune2000" -p:LauncherIcon="../d2k.ico" -p:ModID="d2k" -p:DisplayName="Dune 2000" -p:FaqUrl="http://wiki.openra.net/FAQ"
|
||||||
- move /Y %APPVEYOR_BUILD_FOLDER%\bin\* %APPVEYOR_BUILD_FOLDER%
|
- move /Y %APPVEYOR_BUILD_FOLDER%\bin\* %APPVEYOR_BUILD_FOLDER%
|
||||||
- ps: (Get-Content "${env:APPVEYOR_BUILD_FOLDER}\packaging\windows\WindowsLauncher.cs.in").replace('DISPLAY_NAME', 'Red Alert').replace('MOD_ID', 'ra').replace('FAQ_URL', 'http://wiki.openra.net/FAQ') | Set-Content "${env:APPVEYOR_BUILD_FOLDER}\packaging\windows\WindowsLauncher.cs"
|
|
||||||
- ps: C:\"Program Files (x86)"\"Microsoft Visual Studio"\2017\Community\MSBuild\15.0\Bin\Roslyn\csc.exe /noconfig /platform:x64 /reference:System.dll /reference:System.Core.dll /reference:System.Drawing.dll /reference:System.Windows.Forms.dll /reference:"${env:APPVEYOR_BUILD_FOLDER}\OpenRA.Game.dll" /out:"${env:APPVEYOR_BUILD_FOLDER}\RedAlert.exe" /win32icon:"${env:APPVEYOR_BUILD_FOLDER}\ra.ico" /target:winexe ${env:APPVEYOR_BUILD_FOLDER}\packaging\windows\WindowsLauncher.cs
|
|
||||||
- ps: (Get-Content "${env:APPVEYOR_BUILD_FOLDER}\packaging\windows\WindowsLauncher.cs.in").replace('DISPLAY_NAME', 'Tiberian Dawn').replace('MOD_ID', 'cnc').replace('FAQ_URL', 'http://wiki.openra.net/FAQ') | Set-Content "${env:APPVEYOR_BUILD_FOLDER}\packaging\windows\WindowsLauncher.cs"
|
|
||||||
- ps: C:\"Program Files (x86)"\"Microsoft Visual Studio"\2017\Community\MSBuild\15.0\Bin\Roslyn\csc.exe /noconfig /platform:x64 /reference:System.dll /reference:System.Core.dll /reference:System.Drawing.dll /reference:System.Windows.Forms.dll /reference:"${env:APPVEYOR_BUILD_FOLDER}\OpenRA.Game.dll" /out:"${env:APPVEYOR_BUILD_FOLDER}\TiberianDawn.exe" /win32icon:"${env:APPVEYOR_BUILD_FOLDER}\cnc.ico" /target:winexe ${env:APPVEYOR_BUILD_FOLDER}\packaging\windows\WindowsLauncher.cs
|
|
||||||
- ps: (Get-Content "${env:APPVEYOR_BUILD_FOLDER}\packaging\windows\WindowsLauncher.cs.in").replace('DISPLAY_NAME', 'Dune 2000').replace('MOD_ID', 'd2k').replace('FAQ_URL', 'http://wiki.openra.net/FAQ') | Set-Content "${env:APPVEYOR_BUILD_FOLDER}\packaging\windows\WindowsLauncher.cs"
|
|
||||||
- ps: C:\"Program Files (x86)"\"Microsoft Visual Studio"\2017\Community\MSBuild\15.0\Bin\Roslyn\csc.exe /noconfig /platform:x64 /reference:System.dll /reference:System.Core.dll /reference:System.Drawing.dll /reference:System.Windows.Forms.dll /reference:"${env:APPVEYOR_BUILD_FOLDER}\OpenRA.Game.dll" /out:"${env:APPVEYOR_BUILD_FOLDER}\Dune2000.exe" /win32icon:"${env:APPVEYOR_BUILD_FOLDER}\d2k.ico" /target:winexe ${env:APPVEYOR_BUILD_FOLDER}\packaging\windows\WindowsLauncher.cs
|
|
||||||
- if defined APPVEYOR_REPO_TAG_NAME set VERSION=%APPVEYOR_REPO_TAG_NAME%
|
- if defined APPVEYOR_REPO_TAG_NAME set VERSION=%APPVEYOR_REPO_TAG_NAME%
|
||||||
- if not defined APPVEYOR_REPO_TAG_NAME set VERSION=%APPVEYOR_REPO_COMMIT:~0,7%
|
- if not defined APPVEYOR_REPO_TAG_NAME set VERSION=%APPVEYOR_REPO_COMMIT:~0,7%
|
||||||
- '"C:\Program Files (x86)\NSIS\makensis.exe" /DSRCDIR="%APPVEYOR_BUILD_FOLDER%" /DTAG="git-%VERSION%" /DSUFFIX=" (dev)" /V3 packaging/windows/OpenRA.nsi'
|
- '"C:\Program Files (x86)\NSIS\makensis.exe" /DSRCDIR="%APPVEYOR_BUILD_FOLDER%" /DTAG="git-%VERSION%" /DSUFFIX=" (dev)" /V3 packaging/windows/OpenRA.nsi'
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
#region Copyright & License Information
|
|
||||||
/*
|
|
||||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
|
||||||
* This file is part of OpenRA, which is free software. It is made
|
|
||||||
* available to you under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version. For more
|
|
||||||
* information, see COPYING.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace OpenRA.PostProcess
|
|
||||||
{
|
|
||||||
class Program
|
|
||||||
{
|
|
||||||
static void Main(string[] args)
|
|
||||||
{
|
|
||||||
var assembly = args.First();
|
|
||||||
var flags = args.Skip(1).ToArray();
|
|
||||||
|
|
||||||
Console.WriteLine("Post-processing {0}", assembly);
|
|
||||||
var data = File.ReadAllBytes(assembly);
|
|
||||||
var peOffset = BitConverter.ToInt32(data, 0x3c);
|
|
||||||
|
|
||||||
// Set /LARGEADDRESSAWARE Flag (Application can handle large (>2GB) addresses)
|
|
||||||
Console.WriteLine(" - Enabling /LARGEADDRESSAWARE");
|
|
||||||
data[peOffset + 4 + 18] |= 0x20;
|
|
||||||
|
|
||||||
File.WriteAllBytes(args[0], data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
19
packaging/windows/MakeLAA.py
Normal file
19
packaging/windows/MakeLAA.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
||||||
|
# This file is part of OpenRA, which is free software. It is made
|
||||||
|
# available to you under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation, either version 3 of
|
||||||
|
# the License, or (at your option) any later version. For more
|
||||||
|
# information, see COPYING.
|
||||||
|
|
||||||
|
import struct
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(sys.argv[1] + ': Enabling /LARGEADDRESSAWARE')
|
||||||
|
with open(sys.argv[1], 'r+b') as assembly:
|
||||||
|
assembly.seek(0x3c)
|
||||||
|
peOffset = struct.unpack('i', assembly.read(4))[0]
|
||||||
|
assembly.seek(peOffset + 4 + 18)
|
||||||
|
flags = struct.unpack('B', assembly.read(1))[0] | 0x20
|
||||||
|
assembly.seek(peOffset + 4 + 18)
|
||||||
|
assembly.write(struct.pack('B', flags))
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
#region Copyright & License Information
|
|
||||||
/*
|
|
||||||
* Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
|
|
||||||
* This file is part of OpenRA, which is free software. It is made
|
|
||||||
* available to you under the terms of the GNU General Public License
|
|
||||||
* as published by the Free Software Foundation, either version 3 of
|
|
||||||
* the License, or (at your option) any later version. For more
|
|
||||||
* information, see COPYING.
|
|
||||||
*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Media;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Windows.Forms;
|
|
||||||
|
|
||||||
namespace OpenRA
|
|
||||||
{
|
|
||||||
class WindowsLauncher
|
|
||||||
{
|
|
||||||
static Process gameProcess;
|
|
||||||
|
|
||||||
// Constants to be replaced by the wrapper / compilation script
|
|
||||||
const string ModID = "MOD_ID";
|
|
||||||
const string DisplayName = "DISPLAY_NAME";
|
|
||||||
const string FaqUrl = "FAQ_URL";
|
|
||||||
|
|
||||||
[STAThread]
|
|
||||||
static int Main(string[] args)
|
|
||||||
{
|
|
||||||
if (args.Any(x => x.StartsWith("Engine.LaunchPath=", StringComparison.Ordinal)))
|
|
||||||
return RunGame(args);
|
|
||||||
|
|
||||||
return RunInnerLauncher(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int RunGame(string[] args)
|
|
||||||
{
|
|
||||||
var launcherPath = Assembly.GetExecutingAssembly().Location;
|
|
||||||
var directory = Path.GetDirectoryName(launcherPath);
|
|
||||||
Directory.SetCurrentDirectory(directory);
|
|
||||||
|
|
||||||
AppDomain.CurrentDomain.UnhandledException += (_, e) => ExceptionHandler.HandleFatalError((Exception)e.ExceptionObject);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return (int)Game.InitializeAndRun(args);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
ExceptionHandler.HandleFatalError(e);
|
|
||||||
return (int)RunStatus.Error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int RunInnerLauncher(string[] args)
|
|
||||||
{
|
|
||||||
var launcherPath = Assembly.GetExecutingAssembly().Location;
|
|
||||||
var launcherArgs = args.ToList();
|
|
||||||
|
|
||||||
if (!launcherArgs.Any(x => x.StartsWith("Engine.LaunchPath=", StringComparison.Ordinal)))
|
|
||||||
launcherArgs.Add("Engine.LaunchPath=\"" + launcherPath + "\"");
|
|
||||||
|
|
||||||
if (!launcherArgs.Any(x => x.StartsWith("Game.Mod=", StringComparison.Ordinal)))
|
|
||||||
launcherArgs.Add("Game.Mod=" + ModID);
|
|
||||||
|
|
||||||
var psi = new ProcessStartInfo(launcherPath, string.Join(" ", launcherArgs));
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
gameProcess = Process.Start(psi);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gameProcess == null)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
gameProcess.EnableRaisingEvents = true;
|
|
||||||
gameProcess.Exited += GameProcessExited;
|
|
||||||
|
|
||||||
Application.Run();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ShowErrorDialog()
|
|
||||||
{
|
|
||||||
var headerLabel = new Label
|
|
||||||
{
|
|
||||||
Location = new Point(0, 10),
|
|
||||||
Height = 15,
|
|
||||||
Text = DisplayName + " has encountered a fatal error and must close.",
|
|
||||||
TextAlign = ContentAlignment.TopCenter
|
|
||||||
};
|
|
||||||
|
|
||||||
var docsLabel = new Label
|
|
||||||
{
|
|
||||||
Location = new Point(0, 25),
|
|
||||||
Height = 15,
|
|
||||||
Text = "Refer to the crash logs and FAQ for more information.",
|
|
||||||
TextAlign = ContentAlignment.TopCenter
|
|
||||||
};
|
|
||||||
|
|
||||||
int formWidth;
|
|
||||||
using (var g = headerLabel.CreateGraphics())
|
|
||||||
{
|
|
||||||
var headerWidth = (int)g.MeasureString(headerLabel.Text, headerLabel.Font).Width + 60;
|
|
||||||
var docsWidth = (int)g.MeasureString(docsLabel.Text, docsLabel.Font).Width + 60;
|
|
||||||
formWidth = Math.Max(headerWidth, docsWidth);
|
|
||||||
headerLabel.Width = formWidth;
|
|
||||||
docsLabel.Width = formWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
var form = new Form
|
|
||||||
{
|
|
||||||
Size = new Size(formWidth, 110),
|
|
||||||
Text = "Fatal Error",
|
|
||||||
MinimizeBox = false,
|
|
||||||
MaximizeBox = false,
|
|
||||||
FormBorderStyle = FormBorderStyle.FixedDialog,
|
|
||||||
StartPosition = FormStartPosition.CenterScreen,
|
|
||||||
TopLevel = true,
|
|
||||||
Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location)
|
|
||||||
};
|
|
||||||
|
|
||||||
var viewLogs = new Button
|
|
||||||
{
|
|
||||||
Location = new Point(10, 50),
|
|
||||||
Size = new Size(75, 23),
|
|
||||||
Text = "View Logs"
|
|
||||||
};
|
|
||||||
|
|
||||||
var viewFaq = new Button
|
|
||||||
{
|
|
||||||
Location = new Point(90, 50),
|
|
||||||
Size = new Size(75, 23),
|
|
||||||
Text = "View FAQ"
|
|
||||||
};
|
|
||||||
|
|
||||||
var quit = new Button
|
|
||||||
{
|
|
||||||
Location = new Point(formWidth - 90, 50),
|
|
||||||
Size = new Size(75, 23),
|
|
||||||
Text = "Quit",
|
|
||||||
DialogResult = DialogResult.Cancel
|
|
||||||
};
|
|
||||||
|
|
||||||
form.Controls.Add(headerLabel);
|
|
||||||
form.Controls.Add(docsLabel);
|
|
||||||
form.Controls.Add(viewLogs);
|
|
||||||
form.Controls.Add(viewFaq);
|
|
||||||
form.Controls.Add(quit);
|
|
||||||
|
|
||||||
viewLogs.Click += ViewLogsClicked;
|
|
||||||
viewFaq.Click += ViewFaqClicked;
|
|
||||||
form.FormClosed += FormClosed;
|
|
||||||
|
|
||||||
SystemSounds.Exclamation.Play();
|
|
||||||
form.ShowDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void GameProcessExited(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
if (gameProcess.ExitCode != (int)RunStatus.Success)
|
|
||||||
ShowErrorDialog();
|
|
||||||
|
|
||||||
Exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ViewLogsClicked(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Process.Start(Path.Combine(Platform.SupportDir, "Logs"));
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ViewFaqClicked(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Process.Start(FaqUrl);
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FormClosed(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
Exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Exit()
|
|
||||||
{
|
|
||||||
Environment.Exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
command -v curl >/dev/null 2>&1 || { echo >&2 "Windows packaging requires curl."; exit 1; }
|
command -v curl >/dev/null 2>&1 || { echo >&2 "Windows packaging requires curl."; exit 1; }
|
||||||
command -v makensis >/dev/null 2>&1 || { echo >&2 "Windows packaging requires makensis."; exit 1; }
|
command -v makensis >/dev/null 2>&1 || { echo >&2 "Windows packaging requires makensis."; exit 1; }
|
||||||
command -v convert >/dev/null 2>&1 || { echo >&2 "Windows packaging requires ImageMagick."; exit 1; }
|
command -v convert >/dev/null 2>&1 || { echo >&2 "Windows packaging requires ImageMagick."; exit 1; }
|
||||||
|
command -v python3 >/dev/null 2>&1 || { echo >&2 "Windows packaging requires python 3."; exit 1; }
|
||||||
|
|
||||||
if [ $# -ne "2" ]; then
|
if [ $# -ne "2" ]; then
|
||||||
echo "Usage: $(basename "$0") tag outputdir"
|
echo "Usage: $(basename "$0") tag outputdir"
|
||||||
@@ -38,20 +39,15 @@ function makelauncher()
|
|||||||
# Create multi-resolution icon
|
# Create multi-resolution icon
|
||||||
convert "${ARTWORK_DIR}/${MOD_ID}_16x16.png" "${ARTWORK_DIR}/${MOD_ID}_24x24.png" "${ARTWORK_DIR}/${MOD_ID}_32x32.png" "${ARTWORK_DIR}/${MOD_ID}_48x48.png" "${ARTWORK_DIR}/${MOD_ID}_256x256.png" "${BUILTDIR}/${MOD_ID}.ico"
|
convert "${ARTWORK_DIR}/${MOD_ID}_16x16.png" "${ARTWORK_DIR}/${MOD_ID}_24x24.png" "${ARTWORK_DIR}/${MOD_ID}_32x32.png" "${ARTWORK_DIR}/${MOD_ID}_48x48.png" "${ARTWORK_DIR}/${MOD_ID}_256x256.png" "${BUILTDIR}/${MOD_ID}.ico"
|
||||||
|
|
||||||
sed "s|DISPLAY_NAME|${DISPLAY_NAME}|" WindowsLauncher.cs.in | sed "s|MOD_ID|${MOD_ID}|" | sed "s|FAQ_URL|${FAQ_URL}|" > WindowsLauncher.cs
|
# Create mod-specific launcher
|
||||||
csc WindowsLauncher.cs -warn:4 -warnaserror -platform:"${PLATFORM}" -out:"${BUILTDIR}/${LAUNCHER_NAME}" -t:winexe ${LAUNCHER_LIBS} -win32icon:"${BUILTDIR}/${MOD_ID}.ico"
|
msbuild -t:Build "${SRCDIR}/OpenRA.WindowsLauncher/OpenRA.WindowsLauncher.csproj" -restore -p:Configuration=Release -p:TargetPlatform="${PLATFORM}" -p:LauncherName="${LAUNCHER_NAME}" -p:LauncherIcon="${BUILTDIR}/${MOD_ID}.ico" -p:ModID="${MOD_ID}" -p:DisplayName="${DISPLAY_NAME}" -p:FaqUrl="${FAQ_URL}"
|
||||||
rm WindowsLauncher.cs
|
cp "${SRCDIR}/bin/${LAUNCHER_NAME}.exe" "${BUILTDIR}"
|
||||||
|
cp "${SRCDIR}/bin/${LAUNCHER_NAME}.exe.config" "${BUILTDIR}"
|
||||||
|
|
||||||
# We need to set the loadFromRemoteSources flag for the launcher, but only for the "portable" zip package.
|
# Enable the full 4GB address space for the 32 bit game executable
|
||||||
# Windows automatically un-trusts executables that are extracted from a downloaded zip file
|
# The server and utility do not use enough memory to need this
|
||||||
cp "${BUILTDIR}/OpenRA.exe.config" "${BUILTDIR}/${LAUNCHER_NAME}.config"
|
if [ "${PLATFORM}" = "win-x86" ]; then
|
||||||
|
python3 MakeLAA.py "${BUILTDIR}/${LAUNCHER_NAME}.exe"
|
||||||
if [ "${PLATFORM}" = "x86" ]; then
|
|
||||||
# Enable the full 4GB address space for the 32 bit game executable
|
|
||||||
# The server and utility do not use enough memory to need this
|
|
||||||
csc MakeLAA.cs -warn:4 -warnaserror -out:"MakeLAA.exe"
|
|
||||||
mono "MakeLAA.exe" "${BUILTDIR}/${LAUNCHER_NAME}"
|
|
||||||
rm MakeLAA.exe
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,32 +56,29 @@ function build_platform()
|
|||||||
PLATFORM="${1}"
|
PLATFORM="${1}"
|
||||||
|
|
||||||
echo "Building core files (${PLATFORM})"
|
echo "Building core files (${PLATFORM})"
|
||||||
if [ "${PLATFORM}" = "x86" ]; then
|
if [ "${PLATFORM}" = "win-x86" ]; then
|
||||||
TARGETPLATFORM="TARGETPLATFORM=win-x86"
|
|
||||||
IS_WIN32="WIN32=true"
|
|
||||||
USE_PROGRAMFILES32="-DUSE_PROGRAMFILES32=true"
|
USE_PROGRAMFILES32="-DUSE_PROGRAMFILES32=true"
|
||||||
else
|
else
|
||||||
IS_WIN32="WIN32=false"
|
|
||||||
TARGETPLATFORM="TARGETPLATFORM=win-x64"
|
|
||||||
USE_PROGRAMFILES32=""
|
USE_PROGRAMFILES32=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
pushd "${SRCDIR}" > /dev/null || exit 1
|
pushd "${SRCDIR}" > /dev/null || exit 1
|
||||||
make clean
|
make clean
|
||||||
make core "${TARGETPLATFORM}" "${IS_WIN32}"
|
make core TARGETPLATFORM="${PLATFORM}"
|
||||||
make version VERSION="${TAG}"
|
make version VERSION="${TAG}"
|
||||||
make install-engine "${TARGETPLATFORM}" gameinstalldir="" DESTDIR="${BUILTDIR}"
|
make install-engine TARGETPLATFORM="${PLATFORM}" gameinstalldir="" DESTDIR="${BUILTDIR}"
|
||||||
make install-common-mod-files gameinstalldir="" DESTDIR="${BUILTDIR}"
|
make install-common-mod-files gameinstalldir="" DESTDIR="${BUILTDIR}"
|
||||||
make install-default-mods gameinstalldir="" DESTDIR="${BUILTDIR}"
|
make install-default-mods gameinstalldir="" DESTDIR="${BUILTDIR}"
|
||||||
make install-dependencies "${TARGETPLATFORM}" gameinstalldir="" DESTDIR="${BUILTDIR}"
|
make install-dependencies TARGETPLATFORM="${PLATFORM}" gameinstalldir="" DESTDIR="${BUILTDIR}"
|
||||||
popd > /dev/null || exit 1
|
popd > /dev/null || exit 1
|
||||||
|
|
||||||
cp "${SRCDIR}/bin/OpenRA.exe.config" "${BUILTDIR}"
|
|
||||||
|
|
||||||
echo "Compiling Windows launchers (${PLATFORM})"
|
echo "Compiling Windows launchers (${PLATFORM})"
|
||||||
makelauncher "RedAlert.exe" "Red Alert" "ra" ${PLATFORM}
|
makelauncher "RedAlert" "Red Alert" "ra" ${PLATFORM}
|
||||||
makelauncher "TiberianDawn.exe" "Tiberian Dawn" "cnc" ${PLATFORM}
|
makelauncher "TiberianDawn" "Tiberian Dawn" "cnc" ${PLATFORM}
|
||||||
makelauncher "Dune2000.exe" "Dune 2000" "d2k" ${PLATFORM}
|
makelauncher "Dune2000" "Dune 2000" "d2k" ${PLATFORM}
|
||||||
|
|
||||||
|
# Remove redundant generic launcher
|
||||||
|
rm "${BUILTDIR}/OpenRA.exe"
|
||||||
|
|
||||||
echo "Building Windows setup.exe ($1)"
|
echo "Building Windows setup.exe ($1)"
|
||||||
makensis -V2 -DSRCDIR="${BUILTDIR}" -DTAG="${TAG}" -DSUFFIX="${SUFFIX}" ${USE_PROGRAMFILES32} OpenRA.nsi
|
makensis -V2 -DSRCDIR="${BUILTDIR}" -DTAG="${TAG}" -DSUFFIX="${SUFFIX}" ${USE_PROGRAMFILES32} OpenRA.nsi
|
||||||
@@ -104,5 +97,5 @@ function build_platform()
|
|||||||
rm -rf "${BUILTDIR}"
|
rm -rf "${BUILTDIR}"
|
||||||
}
|
}
|
||||||
|
|
||||||
build_platform "x86"
|
build_platform "win-x86"
|
||||||
build_platform "x64"
|
build_platform "win-x64"
|
||||||
|
|||||||
Reference in New Issue
Block a user