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:
@@ -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 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"
|
||||
|
||||
Reference in New Issue
Block a user