1#region Copyright & License Information
2/*
3 * Copyright 2007-2020 The OpenRA Developers (see AUTHORS)
4 * This file is part of OpenRA, which is free software. It is made
5 * available to you under the terms of the GNU General Public License
6 * as published by the Free Software Foundation, either version 3 of
7 * the License, or (at your option) any later version. For more
8 * information, see COPYING.
9 */
10#endregion
11
12using System;
13using System.Diagnostics;
14using System.Drawing;
15using System.IO;
16using System.Linq;
17using System.Media;
18using System.Reflection;
19using System.Windows.Forms;
20
21namespace OpenRA
22{
23	class WindowsLauncher
24	{
25		static Process gameProcess;
26
27		// Constants to be replaced by the wrapper / compilation script
28		const string ModID = "MOD_ID";
29		const string DisplayName = "DISPLAY_NAME";
30		const string FaqUrl = "FAQ_URL";
31
32		[STAThread]
33		static int Main(string[] args)
34		{
35			if (args.Any(x => x.StartsWith("Engine.LaunchPath=", StringComparison.Ordinal)))
36				return RunGame(args);
37
38			return RunInnerLauncher(args);
39		}
40
41		static int RunGame(string[] args)
42		{
43			var launcherPath = Assembly.GetExecutingAssembly().Location;
44			var directory = Path.GetDirectoryName(launcherPath);
45			Directory.SetCurrentDirectory(directory);
46
47			AppDomain.CurrentDomain.UnhandledException += (_, e) => ExceptionHandler.HandleFatalError((Exception)e.ExceptionObject);
48
49			try
50			{
51				return (int)Game.InitializeAndRun(args);
52			}
53			catch (Exception e)
54			{
55				ExceptionHandler.HandleFatalError(e);
56				return (int)RunStatus.Error;
57			}
58		}
59
60		static int RunInnerLauncher(string[] args)
61		{
62			var launcherPath = Assembly.GetExecutingAssembly().Location;
63			var launcherArgs = args.ToList();
64
65			if (!launcherArgs.Any(x => x.StartsWith("Engine.LaunchPath=", StringComparison.Ordinal)))
66				launcherArgs.Add("Engine.LaunchPath=\"" + launcherPath + "\"");
67
68			if (!launcherArgs.Any(x => x.StartsWith("Game.Mod=", StringComparison.Ordinal)))
69				launcherArgs.Add("Game.Mod=" + ModID);
70
71			var psi = new ProcessStartInfo(launcherPath, string.Join(" ", launcherArgs));
72
73			try
74			{
75				gameProcess = Process.Start(psi);
76			}
77			catch
78			{
79				return 1;
80			}
81
82			if (gameProcess == null)
83				return 1;
84
85			gameProcess.EnableRaisingEvents = true;
86			gameProcess.Exited += GameProcessExited;
87
88			Application.Run();
89
90			return 0;
91		}
92
93		static void ShowErrorDialog()
94		{
95			var headerLabel = new Label
96			{
97				Location = new Point(0, 10),
98				Height = 15,
99				Text = DisplayName + " has encountered a fatal error and must close.",
100				TextAlign = ContentAlignment.TopCenter
101			};
102
103			var docsLabel = new Label
104			{
105				Location = new Point(0, 25),
106				Height = 15,
107				Text = "Refer to the crash logs and FAQ for more information.",
108				TextAlign = ContentAlignment.TopCenter
109			};
110
111			int formWidth;
112			using (var g = headerLabel.CreateGraphics())
113			{
114				var headerWidth = (int)g.MeasureString(headerLabel.Text, headerLabel.Font).Width + 60;
115				var docsWidth = (int)g.MeasureString(docsLabel.Text, docsLabel.Font).Width + 60;
116				formWidth = Math.Max(headerWidth, docsWidth);
117				headerLabel.Width = formWidth;
118				docsLabel.Width = formWidth;
119			}
120
121			var form = new Form
122			{
123				Size = new Size(formWidth, 110),
124				Text = "Fatal Error",
125				MinimizeBox = false,
126				MaximizeBox = false,
127				FormBorderStyle = FormBorderStyle.FixedDialog,
128				StartPosition = FormStartPosition.CenterScreen,
129				TopLevel = true,
130				Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location)
131			};
132
133			var viewLogs = new Button
134			{
135				Location = new Point(10, 50),
136				Size = new Size(75, 23),
137				Text = "View Logs"
138			};
139
140			var viewFaq = new Button
141			{
142				Location = new Point(90, 50),
143				Size = new Size(75, 23),
144				Text = "View FAQ"
145			};
146
147			var quit = new Button
148			{
149				Location = new Point(formWidth - 90, 50),
150				Size = new Size(75, 23),
151				Text = "Quit",
152				DialogResult = DialogResult.Cancel
153			};
154
155			form.Controls.Add(headerLabel);
156			form.Controls.Add(docsLabel);
157			form.Controls.Add(viewLogs);
158			form.Controls.Add(viewFaq);
159			form.Controls.Add(quit);
160
161			viewLogs.Click += ViewLogsClicked;
162			viewFaq.Click += ViewFaqClicked;
163			form.FormClosed += FormClosed;
164
165			SystemSounds.Exclamation.Play();
166			form.ShowDialog();
167		}
168
169		static void GameProcessExited(object sender, EventArgs e)
170		{
171			if (gameProcess.ExitCode != (int)RunStatus.Success)
172				ShowErrorDialog();
173
174			Exit();
175		}
176
177		static void ViewLogsClicked(object sender, EventArgs e)
178		{
179			try
180			{
181				Process.Start(Platform.ResolvePath("^", "Logs"));
182			}
183			catch { }
184		}
185
186		static void ViewFaqClicked(object sender, EventArgs e)
187		{
188			try
189			{
190				Process.Start(FaqUrl);
191			}
192			catch { }
193		}
194
195		static void FormClosed(object sender, EventArgs e)
196		{
197			Exit();
198		}
199
200		static void Exit()
201		{
202			Environment.Exit(0);
203		}
204	}
205}
206