diff --git a/Makefile b/Makefile
index a00c4caedd..5a5ded0a60 100644
--- a/Makefile
+++ b/Makefile
@@ -76,9 +76,6 @@ INSTALL_DATA = $(INSTALL) -m644
# Toolchain
MSBUILD = msbuild -verbosity:m -nologo
-# Enable 32 bit builds while generating the windows installer
-WIN32 = false
-
# dependencies
ifndef TARGETPLATFORM
UNAME_S := $(shell uname -s)
diff --git a/OpenRA.WindowsLauncher/App.config b/OpenRA.WindowsLauncher/App.config
new file mode 100644
index 0000000000..cab8f010ca
--- /dev/null
+++ b/OpenRA.WindowsLauncher/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenRA.WindowsLauncher/OpenRA.WindowsLauncher.csproj b/OpenRA.WindowsLauncher/OpenRA.WindowsLauncher.csproj
new file mode 100644
index 0000000000..79233e8625
--- /dev/null
+++ b/OpenRA.WindowsLauncher/OpenRA.WindowsLauncher.csproj
@@ -0,0 +1,61 @@
+
+
+ winexe
+ net472
+ true
+ true
+ false
+ 7.3
+ true
+ true
+ false
+ ../bin
+ false
+ AnyCPU
+ false
+ false
+ ..\OpenRA.ruleset
+ Release;Debug
+ $(LauncherName)
+ $(LauncherIcon)
+
+
+
+
+
+
+ DEBUG;TRACE
+ false
+
+
+ true
+
+
+
+
+
+ <_Parameter1>ModID
+ <_Parameter2>$(ModID)
+
+
+ <_Parameter1>DisplayName
+ <_Parameter2>$(DisplayName)
+
+
+ <_Parameter1>FaqUrl
+ <_Parameter2>$(FaqUrl)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/OpenRA.WindowsLauncher/Program.cs b/OpenRA.WindowsLauncher/Program.cs
new file mode 100644
index 0000000000..ae1a98a144
--- /dev/null
+++ b/OpenRA.WindowsLauncher/Program.cs
@@ -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);
+ }
+ }
+}
diff --git a/OpenRA.sln b/OpenRA.sln
index 1be8c442e7..6b732e68e2 100644
--- a/OpenRA.sln
+++ b/OpenRA.sln
@@ -21,6 +21,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenRA.Test", "OpenRA.Test\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenRA.Launcher", "OpenRA.Launcher\OpenRA.Launcher.csproj", "{54DAE0E0-3125-49D3-992E-A0E931EB5FC8}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenRA.WindowsLauncher", "OpenRA.WindowsLauncher\OpenRA.WindowsLauncher.csproj", "{912B578E-0BC0-4987-807B-1E294A87DA37}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
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}.Release|Any CPU.ActiveCfg = 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
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/appveyor.yml b/appveyor.yml
index 324d22cb68..f81557015a 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -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/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')"
+ - 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%
- - 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 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'
diff --git a/packaging/windows/MakeLAA.cs b/packaging/windows/MakeLAA.cs
deleted file mode 100644
index d03fcb55ff..0000000000
--- a/packaging/windows/MakeLAA.cs
+++ /dev/null
@@ -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);
- }
- }
-}
diff --git a/packaging/windows/MakeLAA.py b/packaging/windows/MakeLAA.py
new file mode 100644
index 0000000000..c66f81fd2e
--- /dev/null
+++ b/packaging/windows/MakeLAA.py
@@ -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))
diff --git a/packaging/windows/WindowsLauncher.cs.in b/packaging/windows/WindowsLauncher.cs.in
deleted file mode 100644
index 2a370e928b..0000000000
--- a/packaging/windows/WindowsLauncher.cs.in
+++ /dev/null
@@ -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);
- }
- }
-}
diff --git a/packaging/windows/buildpackage.sh b/packaging/windows/buildpackage.sh
index 92c7309b55..040c145575 100755
--- a/packaging/windows/buildpackage.sh
+++ b/packaging/windows/buildpackage.sh
@@ -3,6 +3,7 @@
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 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
echo "Usage: $(basename "$0") tag outputdir"
@@ -38,20 +39,15 @@ function makelauncher()
# 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"
- sed "s|DISPLAY_NAME|${DISPLAY_NAME}|" WindowsLauncher.cs.in | sed "s|MOD_ID|${MOD_ID}|" | sed "s|FAQ_URL|${FAQ_URL}|" > WindowsLauncher.cs
- csc WindowsLauncher.cs -warn:4 -warnaserror -platform:"${PLATFORM}" -out:"${BUILTDIR}/${LAUNCHER_NAME}" -t:winexe ${LAUNCHER_LIBS} -win32icon:"${BUILTDIR}/${MOD_ID}.ico"
- rm WindowsLauncher.cs
+ # Create mod-specific launcher
+ 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}"
+ 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.
- # Windows automatically un-trusts executables that are extracted from a downloaded zip file
- cp "${BUILTDIR}/OpenRA.exe.config" "${BUILTDIR}/${LAUNCHER_NAME}.config"
-
- 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
+ # Enable the full 4GB address space for the 32 bit game executable
+ # The server and utility do not use enough memory to need this
+ if [ "${PLATFORM}" = "win-x86" ]; then
+ python3 MakeLAA.py "${BUILTDIR}/${LAUNCHER_NAME}.exe"
fi
}
@@ -60,32 +56,29 @@ function build_platform()
PLATFORM="${1}"
echo "Building core files (${PLATFORM})"
- if [ "${PLATFORM}" = "x86" ]; then
- TARGETPLATFORM="TARGETPLATFORM=win-x86"
- IS_WIN32="WIN32=true"
+ if [ "${PLATFORM}" = "win-x86" ]; then
USE_PROGRAMFILES32="-DUSE_PROGRAMFILES32=true"
else
- IS_WIN32="WIN32=false"
- TARGETPLATFORM="TARGETPLATFORM=win-x64"
USE_PROGRAMFILES32=""
fi
pushd "${SRCDIR}" > /dev/null || exit 1
make clean
- make core "${TARGETPLATFORM}" "${IS_WIN32}"
+ make core TARGETPLATFORM="${PLATFORM}"
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-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
- cp "${SRCDIR}/bin/OpenRA.exe.config" "${BUILTDIR}"
-
echo "Compiling Windows launchers (${PLATFORM})"
- makelauncher "RedAlert.exe" "Red Alert" "ra" ${PLATFORM}
- makelauncher "TiberianDawn.exe" "Tiberian Dawn" "cnc" ${PLATFORM}
- makelauncher "Dune2000.exe" "Dune 2000" "d2k" ${PLATFORM}
+ makelauncher "RedAlert" "Red Alert" "ra" ${PLATFORM}
+ makelauncher "TiberianDawn" "Tiberian Dawn" "cnc" ${PLATFORM}
+ makelauncher "Dune2000" "Dune 2000" "d2k" ${PLATFORM}
+
+ # Remove redundant generic launcher
+ rm "${BUILTDIR}/OpenRA.exe"
echo "Building Windows setup.exe ($1)"
makensis -V2 -DSRCDIR="${BUILTDIR}" -DTAG="${TAG}" -DSUFFIX="${SUFFIX}" ${USE_PROGRAMFILES32} OpenRA.nsi
@@ -104,5 +97,5 @@ function build_platform()
rm -rf "${BUILTDIR}"
}
-build_platform "x86"
-build_platform "x64"
+build_platform "win-x86"
+build_platform "win-x64"