Bring Windows launcher into line with pchote's ideas.

This commit is contained in:
Matthew Bowra-Dean
2010-11-18 23:41:13 +13:00
committed by Paul Chote
parent 085685a769
commit d2a52fd529
13 changed files with 409 additions and 1058 deletions

View File

@@ -1,233 +0,0 @@
namespace OpenRA.Launcher
{
partial class ConfigureModsDialog
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.cancelButton = new System.Windows.Forms.Button();
this.okButton = new System.Windows.Forms.Button();
this.installButton = new System.Windows.Forms.Button();
this.installModDialog = new System.Windows.Forms.OpenFileDialog();
this.treeView1 = new System.Windows.Forms.TreeView();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.addButton = new System.Windows.Forms.Button();
this.removeButton = new System.Windows.Forms.Button();
this.listView1 = new System.Windows.Forms.ListView();
this.columnHeader1 = new System.Windows.Forms.ColumnHeader();
this.label1 = new System.Windows.Forms.Label();
this.propertyGrid1 = new System.Windows.Forms.PropertyGrid();
this.label2 = new System.Windows.Forms.Label();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// cancelButton
//
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(446, 477);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 23);
this.cancelButton.TabIndex = 0;
this.cancelButton.Text = "Cancel";
this.cancelButton.UseVisualStyleBackColor = true;
//
// okButton
//
this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.okButton.Location = new System.Drawing.Point(365, 477);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 23);
this.okButton.TabIndex = 1;
this.okButton.Text = "OK";
this.okButton.UseVisualStyleBackColor = true;
//
// installButton
//
this.installButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.installButton.Location = new System.Drawing.Point(12, 477);
this.installButton.Name = "installButton";
this.installButton.Size = new System.Drawing.Size(116, 23);
this.installButton.TabIndex = 2;
this.installButton.Text = "Install Mod...";
this.installButton.UseVisualStyleBackColor = true;
this.installButton.Click += new System.EventHandler(this.InstallMod);
//
// installModDialog
//
this.installModDialog.Filter = "Zip files|*.zip";
this.installModDialog.RestoreDirectory = true;
//
// treeView1
//
this.treeView1.Dock = System.Windows.Forms.DockStyle.Fill;
this.treeView1.Location = new System.Drawing.Point(3, 23);
this.treeView1.Name = "treeView1";
this.tableLayoutPanel1.SetRowSpan(this.treeView1, 2);
this.treeView1.Size = new System.Drawing.Size(233, 212);
this.treeView1.TabIndex = 3;
this.treeView1.Enter += new System.EventHandler(this.treeView1_Enter);
this.treeView1.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.TreeViewSelect);
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.tableLayoutPanel1.ColumnCount = 3;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 30F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.Controls.Add(this.treeView1, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.addButton, 1, 1);
this.tableLayoutPanel1.Controls.Add(this.removeButton, 2, 1);
this.tableLayoutPanel1.Controls.Add(this.listView1, 2, 1);
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.propertyGrid1, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.label2, 2, 0);
this.tableLayoutPanel1.Location = new System.Drawing.Point(12, 12);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 4;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(509, 459);
this.tableLayoutPanel1.TabIndex = 4;
//
// addButton
//
this.addButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
this.addButton.Location = new System.Drawing.Point(242, 103);
this.addButton.Name = "addButton";
this.addButton.Size = new System.Drawing.Size(24, 23);
this.addButton.TabIndex = 5;
this.addButton.Text = "+";
this.addButton.UseVisualStyleBackColor = true;
this.addButton.Click += new System.EventHandler(this.ActivateMod);
//
// removeButton
//
this.removeButton.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.removeButton.Location = new System.Drawing.Point(242, 132);
this.removeButton.Name = "removeButton";
this.removeButton.Size = new System.Drawing.Size(24, 23);
this.removeButton.TabIndex = 6;
this.removeButton.Text = "-";
this.removeButton.UseVisualStyleBackColor = true;
this.removeButton.Click += new System.EventHandler(this.DeactivateMod);
//
// listView1
//
this.listView1.Activation = System.Windows.Forms.ItemActivation.OneClick;
this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
this.columnHeader1});
this.listView1.Dock = System.Windows.Forms.DockStyle.Fill;
this.listView1.FullRowSelect = true;
this.listView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
this.listView1.Location = new System.Drawing.Point(272, 23);
this.listView1.MultiSelect = false;
this.listView1.Name = "listView1";
this.tableLayoutPanel1.SetRowSpan(this.listView1, 2);
this.listView1.Size = new System.Drawing.Size(234, 212);
this.listView1.TabIndex = 8;
this.listView1.TileSize = new System.Drawing.Size(10, 10);
this.listView1.UseCompatibleStateImageBehavior = false;
this.listView1.View = System.Windows.Forms.View.Details;
this.listView1.SelectedIndexChanged += new System.EventHandler(this.ListViewSelect);
this.listView1.Enter += new System.EventHandler(this.ListViewSelect);
//
// label1
//
this.label1.Anchor = System.Windows.Forms.AnchorStyles.None;
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(96, 3);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(47, 13);
this.label1.TabIndex = 10;
this.label1.Text = "All Mods";
//
// propertyGrid1
//
this.tableLayoutPanel1.SetColumnSpan(this.propertyGrid1, 3);
this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill;
this.propertyGrid1.Location = new System.Drawing.Point(3, 241);
this.propertyGrid1.Name = "propertyGrid1";
this.propertyGrid1.PropertySort = System.Windows.Forms.PropertySort.NoSort;
this.propertyGrid1.Size = new System.Drawing.Size(503, 215);
this.propertyGrid1.TabIndex = 9;
this.propertyGrid1.ToolbarVisible = false;
//
// label2
//
this.label2.Anchor = System.Windows.Forms.AnchorStyles.None;
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(356, 3);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(66, 13);
this.label2.TabIndex = 11;
this.label2.Text = "Active Mods";
//
// ConfigureModsDialog
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(533, 512);
this.Controls.Add(this.tableLayoutPanel1);
this.Controls.Add(this.installButton);
this.Controls.Add(this.okButton);
this.Controls.Add(this.cancelButton);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "ConfigureModsDialog";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Configure Mods";
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button cancelButton;
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.Button installButton;
private System.Windows.Forms.OpenFileDialog installModDialog;
private System.Windows.Forms.TreeView treeView1;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Button addButton;
private System.Windows.Forms.Button removeButton;
private System.Windows.Forms.ListView listView1;
private System.Windows.Forms.PropertyGrid propertyGrid1;
private System.Windows.Forms.ColumnHeader columnHeader1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
}
}

View File

@@ -1,291 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 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. For more information,
* see LICENSE.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.IO.Pipes;
using System.IO;
namespace OpenRA.Launcher
{
public partial class ConfigureModsDialog : Form
{
List<string> activeMods;
public List<string> ActiveMods
{
get { return activeMods; }
}
Dictionary<string, Mod> allMods;
public ConfigureModsDialog(string[] activeMods)
{
InitializeComponent();
Util.UacShield(installButton);
this.activeMods = new List<string>(activeMods);
listView1.Items.AddRange(activeMods.Select(x => new ListViewItem(x)).ToArray());
RefreshMods();
}
Mod GetMetadata(string mod)
{
string responseString;
using (var response = UtilityProgram.Call("-i", mod))
{
responseString = response.ReadToEnd();
}
if (Util.IsError(ref responseString)) return null;
string[] lines = responseString.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < lines.Length; i++)
lines[i] = lines[i].Trim('\r');
string title = "", version = "", author = "", description = "", requires = "";
bool standalone = false;
foreach (string line in lines)
{
string s = line.Trim(' ', '\r', '\n');
int i = s.IndexOf(':');
if (i + 2 > s.Length) continue;
string value = s.Substring(i + 2);
switch (s.Substring(0, i))
{
case "Title":
title = value;
break;
case "Version":
version = value;
break;
case "Author":
author = value;
break;
case "Description":
description = value;
break;
case "Requires":
requires = value;
break;
case "Standalone":
standalone = bool.Parse(value);
break;
default:
break;
}
}
return new Mod(title, version, author, description, requires, standalone);
}
void RefreshMods()
{
string responseString;
using (var response = UtilityProgram.Call("--list-mods"))
{
responseString = response.ReadToEnd();
}
string[] mods;
if (!Util.IsError(ref responseString))
mods = responseString.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
else
throw new Exception(string.Format("Could not list mods: {0}", responseString));
for (int i = 0; i < mods.Length; i++)
mods[i] = mods[i].Trim('\r');
allMods = mods.ToDictionary(x => x, x => GetMetadata(x));
RefreshModTree(treeView1, allMods.Keys.ToArray());
}
private void InstallMod(object sender, EventArgs e)
{
if (installModDialog.ShowDialog() != DialogResult.OK) return;
var p = UtilityProgram.CallWithAdmin("--install-mod", installModDialog.FileName);
var pipe = new NamedPipeClientStream(".", "OpenRA.Utility", PipeDirection.In);
pipe.Connect();
p.WaitForExit();
using (var response = new StreamReader(pipe))
{
string s = response.ReadToEnd();
}
RefreshMods();
}
void RefreshModTree(TreeView treeView, string[] modList)
{
treeView.Nodes.Clear();
Dictionary<string, TreeNode> nodes;
nodes = modList.Where(x => allMods[x].Standalone).ToDictionary(x => x, x => new TreeNode(x));
string[] rootMods = modList.Where(x => allMods[x].Standalone).ToArray();
string[] remaining = modList.Except(nodes.Keys).ToArray();
while (remaining.Length > 0)
{
bool progress = false;
List<string> toRemove = new List<string>();
foreach (string s in remaining)
{
var n = new TreeNode(s);
if (allMods[s].Requires == null) continue;
if (!nodes.ContainsKey(allMods[s].Requires)) continue;
nodes[allMods[s].Requires].Nodes.Add(n);
nodes.Add(s, n);
toRemove.Add(s);
progress = true;
}
if (!progress)
break;
remaining = remaining.Except(toRemove).ToArray();
}
foreach (string s in rootMods)
treeView.Nodes.Add(nodes[s]);
if (remaining.Length > 0)
{
var n = new TreeNode("<Unspecified Dependency>");
n.ForeColor = SystemColors.GrayText;
var m = new TreeNode("<Missing Dependency>");
m.ForeColor = SystemColors.GrayText;
foreach (var s in remaining)
{
if (allMods[s].Requires == null)
n.Nodes.Add(new TreeNode(s) { ForeColor = SystemColors.GrayText });
else if (!nodes.ContainsKey(allMods[s].Requires))
m.Nodes.Add(new TreeNode(s) { ForeColor = SystemColors.GrayText });
}
treeView.Nodes.Add(n);
treeView.Nodes.Add(m);
}
treeView.Invalidate();
}
void TreeViewSelect(object sender, TreeViewEventArgs e)
{
SelectMod(e.Node.Text);
}
void ListViewSelect(object sender, EventArgs e)
{
if (listView1.SelectedItems.Count > 0)
SelectMod(listView1.SelectedItems[0].Text);
else
SelectMod("");
}
void treeView1_Enter(object sender, EventArgs e)
{
if (treeView1.SelectedNode != null)
SelectMod(treeView1.SelectedNode.Text);
else
SelectMod("");
}
void SelectMod(string mod)
{
if (!allMods.ContainsKey(mod))
propertyGrid1.SelectedObject = null;
else
propertyGrid1.SelectedObject = allMods[mod];
}
void ActivateMod(object sender, EventArgs e)
{
if (treeView1.SelectedNode == null) return;
string mod = treeView1.SelectedNode.Text;
if (!allMods.ContainsKey(mod)) return;
if (activeMods.Contains(mod)) return;
Mod m = allMods[mod];
Stack<string> toAdd = new Stack<string>();
toAdd.Push(mod);
while (!string.IsNullOrEmpty(m.Requires))
{
string r = m.Requires;
if (!allMods.ContainsKey(r))
{
MessageBox.Show(string.Format("A requirement for the mod \"{0}\" is missing. Please install \"{1}\" or the game may not run properly.", mod, r));
return;
}
if (!activeMods.Contains(r))
toAdd.Push(r);
mod = r;
m = allMods[mod];
}
while (toAdd.Count > 0)
activeMods.Add(toAdd.Pop());
listView1.Items.Clear();
listView1.Items.AddRange(activeMods.Select(x => new ListViewItem(x)).ToArray());
}
void DeactivateMod(object sender, EventArgs e)
{
if (listView1.SelectedItems.Count < 1) return;
string mod = listView1.SelectedItems[0].Text;
List<string> toRemove = new List<string>();
Stack<string> nodes = new Stack<string>();
nodes.Push(mod);
string currentNode;
while (nodes.Count > 0)
{
currentNode = nodes.Pop();
toRemove.Add(currentNode);
foreach (string n in activeMods.Where(x => allMods[x].Requires == currentNode))
nodes.Push(n);
}
listView1.SuspendLayout();
foreach (string s in toRemove)
{
listView1.Items.Remove(listView1.Items.OfType<ListViewItem>().Where(x => x.Text == s).SingleOrDefault());
activeMods.Remove(s);
}
listView1.ResumeLayout();
}
}
class Mod
{
public string Title { get; private set; }
public string Version { get; private set; }
public string Author { get; private set; }
public string Description { get; private set; }
public string Requires { get; private set; }
public bool Standalone { get; private set; }
public Mod(string title, string version, string author, string description, string requires, bool standalone)
{
Title = title;
Version = version;
Author = author;
Description = description;
Requires = requires;
Standalone = standalone;
}
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
namespace OpenRA.Launcher
{
public class JSBridge
{
Dictionary<string, Mod> allMods;
public JSBridge(Dictionary<string, Mod> allMods)
{
this.allMods = allMods;
}
public bool fileExistsInMod(string file, string mod)
{
return File.Exists(string.Format("mods{0}{1}{0}{2}", Path.DirectorySeparatorChar, mod, file));
}
public void log(string message)
{
Console.WriteLine("js: " + message);
}
public void launchMod(string mod)
{
string m = mod;
List<string> modList = new List<string>();
modList.Add(m);
if (!allMods.ContainsKey(m)) System.Windows.Forms.MessageBox.Show("allMods does not contain " + m);
while (!string.IsNullOrEmpty(allMods[m].Requires))
{
m = allMods[m].Requires;
modList.Add(m);
}
Process p = new Process();
p.StartInfo.FileName = "OpenRA.Game.exe";
p.StartInfo.Arguments = "Game.Mods=" + string.Join(",", modList.ToArray());
p.Start();
}
}
}

146
OpenRA.Launcher/Launcher.Designer.cs generated Normal file
View File

@@ -0,0 +1,146 @@
namespace OpenRA.Launcher
{
partial class Launcher
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.Windows.Forms.TreeNode treeNode1 = new System.Windows.Forms.TreeNode("Mods", -2, -2);
System.Windows.Forms.TreeNode treeNode2 = new System.Windows.Forms.TreeNode("Broken Mods");
this.installButton = new System.Windows.Forms.Button();
this.installModDialog = new System.Windows.Forms.OpenFileDialog();
this.treeView = new System.Windows.Forms.TreeView();
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
this.webBrowser = new System.Windows.Forms.WebBrowser();
this.panel1 = new System.Windows.Forms.Panel();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// installButton
//
this.installButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.installButton.Location = new System.Drawing.Point(12, 12);
this.installButton.Name = "installButton";
this.installButton.Size = new System.Drawing.Size(116, 23);
this.installButton.TabIndex = 2;
this.installButton.Text = "Install Mod...";
this.installButton.UseVisualStyleBackColor = true;
this.installButton.Click += new System.EventHandler(this.InstallMod);
//
// installModDialog
//
this.installModDialog.Filter = "Zip files|*.zip";
this.installModDialog.RestoreDirectory = true;
//
// treeView
//
this.treeView.Dock = System.Windows.Forms.DockStyle.Fill;
this.treeView.Location = new System.Drawing.Point(0, 0);
this.treeView.Name = "treeView";
treeNode1.ImageIndex = -2;
treeNode1.Name = "ModsNode";
treeNode1.SelectedImageIndex = -2;
treeNode1.Text = "Mods";
treeNode2.Name = "BrokenModsNode";
treeNode2.Text = "Broken Mods";
this.treeView.Nodes.AddRange(new System.Windows.Forms.TreeNode[] {
treeNode1,
treeNode2});
this.treeView.ShowLines = false;
this.treeView.Size = new System.Drawing.Size(146, 465);
this.treeView.TabIndex = 3;
this.treeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.treeView_AfterSelect);
//
// splitContainer1
//
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer1.Location = new System.Drawing.Point(0, 0);
this.splitContainer1.Name = "splitContainer1";
//
// splitContainer1.Panel1
//
this.splitContainer1.Panel1.Controls.Add(this.treeView);
//
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.webBrowser);
this.splitContainer1.Size = new System.Drawing.Size(671, 465);
this.splitContainer1.SplitterDistance = 146;
this.splitContainer1.TabIndex = 4;
//
// webBrowser
//
this.webBrowser.AllowWebBrowserDrop = false;
this.webBrowser.Dock = System.Windows.Forms.DockStyle.Fill;
this.webBrowser.Location = new System.Drawing.Point(0, 0);
this.webBrowser.MinimumSize = new System.Drawing.Size(20, 20);
this.webBrowser.Name = "webBrowser";
this.webBrowser.Size = new System.Drawing.Size(521, 465);
this.webBrowser.TabIndex = 0;
//
// panel1
//
this.panel1.Controls.Add(this.installButton);
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel1.Location = new System.Drawing.Point(0, 465);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(671, 47);
this.panel1.TabIndex = 5;
//
// Launcher
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(671, 512);
this.Controls.Add(this.splitContainer1);
this.Controls.Add(this.panel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Launcher";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "OpenRA Launcher";
this.splitContainer1.Panel1.ResumeLayout(false);
this.splitContainer1.Panel2.ResumeLayout(false);
this.splitContainer1.ResumeLayout(false);
this.panel1.ResumeLayout(false);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Button installButton;
private System.Windows.Forms.OpenFileDialog installModDialog;
private System.Windows.Forms.TreeView treeView;
private System.Windows.Forms.SplitContainer splitContainer1;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.WebBrowser webBrowser;
}
}

181
OpenRA.Launcher/Launcher.cs Normal file
View File

@@ -0,0 +1,181 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 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. For more information,
* see LICENSE.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.IO.Pipes;
using System.IO;
namespace OpenRA.Launcher
{
public partial class Launcher : Form
{
Dictionary<string, Mod> allMods;
public Launcher()
{
InitializeComponent();
Util.UacShield(installButton);
//treeView.Nodes["ModsNode"].ImageIndex = 1;
//treeView.Nodes["ModsNode"].SelectedImageIndex = 1;
RefreshMods();
webBrowser.ObjectForScripting = new JSBridge(allMods);
}
Mod GetMetadata(string mod)
{
string responseString;
using (var response = UtilityProgram.Call("-i", mod))
{
responseString = response.ReadToEnd();
}
if (Util.IsError(ref responseString)) return null;
string[] lines = responseString.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < lines.Length; i++)
lines[i] = lines[i].Trim('\r');
string title = "", version = "", author = "", description = "", requires = "";
bool standalone = false;
foreach (string line in lines)
{
string s = line.Trim(' ', '\r', '\n');
int i = s.IndexOf(':');
if (i + 2 > s.Length) continue;
string value = s.Substring(i + 2);
switch (s.Substring(0, i))
{
case "Title":
title = value;
break;
case "Version":
version = value;
break;
case "Author":
author = value;
break;
case "Description":
description = value;
break;
case "Requires":
requires = value;
break;
case "Standalone":
standalone = bool.Parse(value);
break;
default:
break;
}
}
return new Mod(title, version, author, description, requires, standalone);
}
void RefreshMods()
{
string responseString;
using (var response = UtilityProgram.Call("--list-mods"))
{
responseString = response.ReadToEnd();
}
string[] mods;
if (!Util.IsError(ref responseString))
mods = responseString.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
else
throw new Exception(string.Format("Could not list mods: {0}", responseString));
for (int i = 0; i < mods.Length; i++)
mods[i] = mods[i].Trim('\r');
allMods = mods.ToDictionary(x => x, x => GetMetadata(x));
RefreshModTree(treeView, allMods.Keys.ToArray());
}
private void InstallMod(object sender, EventArgs e)
{
if (installModDialog.ShowDialog() != DialogResult.OK) return;
var p = UtilityProgram.CallWithAdmin("--install-mod", installModDialog.FileName);
var pipe = new NamedPipeClientStream(".", "OpenRA.Utility", PipeDirection.In);
pipe.Connect();
p.WaitForExit();
using (var response = new StreamReader(pipe))
{
string s = response.ReadToEnd();
}
RefreshMods();
}
void RefreshModTree(TreeView treeView, string[] modList)
{
treeView.Nodes["ModsNode"].Nodes.Clear();
Dictionary<string, TreeNode> nodes;
nodes = modList.Where(x => allMods[x].Standalone).ToDictionary(x => x,
x => new TreeNode(allMods[x].Title) { Name = x });
string[] rootMods = modList.Where(x => allMods[x].Standalone).ToArray();
Stack<string> remaining = new Stack<string>(modList.Except(nodes.Keys));
bool progress = true;
while (remaining.Count > 0 && progress)
{
progress = false;
string s = remaining.Pop();
var n = new TreeNode(allMods[s].Title) { Name = s };
if (allMods[s].Requires == null) { remaining.Push(s); continue; }
if (!nodes.ContainsKey(allMods[s].Requires)) { remaining.Push(s); continue; }
nodes[allMods[s].Requires].Nodes.Add(n);
nodes.Add(s, n);
progress = true;
}
foreach (string s in rootMods)
treeView.Nodes["ModsNode"].Nodes.Add(nodes[s]);
if (remaining.Count > 0)
{
var unspecified = new TreeNode("<Unspecified Dependency>") { ForeColor = SystemColors.GrayText };
var missing = new TreeNode("<Missing Dependency>") { ForeColor = SystemColors.GrayText };
foreach (var s in remaining)
{
if (allMods[s].Requires == null)
unspecified.Nodes.Add(new TreeNode(allMods[s].Title)
{ ForeColor = SystemColors.GrayText, Name = s });
else if (!nodes.ContainsKey(allMods[s].Requires))
missing.Nodes.Add(new TreeNode(allMods[s].Title)
{ ForeColor = SystemColors.GrayText, Name = s });
}
treeView.Nodes["BrokenModsNode"].Nodes.Add(unspecified);
treeView.Nodes["BrokenModsNode"].Nodes.Add(missing);
}
treeView.Nodes["ModsNode"].ExpandAll();
treeView.Invalidate();
}
void treeView_AfterSelect(object sender, TreeViewEventArgs e)
{
Mod selectedMod;
if (!allMods.TryGetValue(e.Node.Name, out selectedMod)) return;
string modHtmlPath = string.Format("mods{0}{1}{0}mod.html", Path.DirectorySeparatorChar, e.Node.Name);
if (!File.Exists(modHtmlPath)) return;
webBrowser.Navigate(Path.GetFullPath(modHtmlPath));
}
}
}

View File

@@ -1,160 +0,0 @@
namespace OpenRA.Launcher
{
partial class MainForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.launchButton = new System.Windows.Forms.Button();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.quitButton = new System.Windows.Forms.Button();
this.configModsButton = new System.Windows.Forms.Button();
this.configGameButton = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// pictureBox1
//
this.pictureBox1.Anchor = System.Windows.Forms.AnchorStyles.None;
this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
this.pictureBox1.Location = new System.Drawing.Point(51, 3);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(192, 64);
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
//
// launchButton
//
this.launchButton.Anchor = System.Windows.Forms.AnchorStyles.None;
this.launchButton.Location = new System.Drawing.Point(51, 97);
this.launchButton.Name = "launchButton";
this.launchButton.Size = new System.Drawing.Size(192, 50);
this.launchButton.TabIndex = 1;
this.launchButton.Text = "Launch OpenRA";
this.launchButton.UseVisualStyleBackColor = true;
this.launchButton.Click += new System.EventHandler(this.LaunchGame);
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.pictureBox1, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.launchButton, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.quitButton, 0, 5);
this.tableLayoutPanel1.Controls.Add(this.configModsButton, 0, 3);
this.tableLayoutPanel1.Controls.Add(this.configGameButton, 0, 4);
this.tableLayoutPanel1.Controls.Add(this.label1, 0, 1);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 6;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 25F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(294, 372);
this.tableLayoutPanel1.TabIndex = 2;
//
// quitButton
//
this.quitButton.Anchor = System.Windows.Forms.AnchorStyles.None;
this.quitButton.Location = new System.Drawing.Point(51, 311);
this.quitButton.Name = "quitButton";
this.quitButton.Size = new System.Drawing.Size(192, 50);
this.quitButton.TabIndex = 2;
this.quitButton.Text = "Quit";
this.quitButton.UseVisualStyleBackColor = true;
//
// configModsButton
//
this.configModsButton.Anchor = System.Windows.Forms.AnchorStyles.None;
this.configModsButton.Location = new System.Drawing.Point(51, 168);
this.configModsButton.Name = "configModsButton";
this.configModsButton.Size = new System.Drawing.Size(192, 50);
this.configModsButton.TabIndex = 3;
this.configModsButton.Text = "Configure Mods...";
this.configModsButton.UseVisualStyleBackColor = true;
this.configModsButton.Click += new System.EventHandler(this.ConfigureMods);
//
// configGameButton
//
this.configGameButton.Anchor = System.Windows.Forms.AnchorStyles.None;
this.configGameButton.Enabled = false;
this.configGameButton.Location = new System.Drawing.Point(51, 239);
this.configGameButton.Name = "configGameButton";
this.configGameButton.Size = new System.Drawing.Size(192, 50);
this.configGameButton.TabIndex = 4;
this.configGameButton.Text = "Configure Game...";
this.configGameButton.UseVisualStyleBackColor = true;
//
// label1
//
this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
this.label1.AutoSize = true;
this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label1.Location = new System.Drawing.Point(3, 70);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(288, 17);
this.label1.TabIndex = 5;
this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(294, 372);
this.Controls.Add(this.tableLayoutPanel1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "MainForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "OpenRA Launcher";
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.Button launchButton;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Button quitButton;
private System.Windows.Forms.Button configModsButton;
private System.Windows.Forms.Button configGameButton;
private System.Windows.Forms.Label label1;
}
}

View File

@@ -1,82 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2010 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. For more information,
* see LICENSE.
*/
#endregion
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Windows.Forms;
namespace OpenRA.Launcher
{
public partial class MainForm : Form
{
string configPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + Path.DirectorySeparatorChar + "OpenRA";
string[] currentMods;
public MainForm()
{
InitializeComponent();
quitButton.Click += (o, e) => { Application.Exit(); };
using (var s = UtilityProgram.Call("--settings-value", configPath, "Game.Mods"))
{
var response = s.ReadToEnd();
if (Util.IsError(ref response))
currentMods = new string[] { "ra" };
else
currentMods = response.Split(',');
}
UpdateModLabel();
}
void UpdateModLabel()
{
label1.Text = string.Format("Current Mods: {0}", currentMods.Length > 0 ? string.Join(",", currentMods) : "ra");
}
void ConfigureMods(object sender, EventArgs e)
{
var d = new ConfigureModsDialog(currentMods);
if (d.ShowDialog() != DialogResult.OK)
return;
currentMods = d.ActiveMods.ToArray();
UpdateModLabel();
}
void LaunchGame(object sender, EventArgs e)
{
string[] officialMods = { "ra", "cnc" };
bool allOk = true;
foreach(string s in officialMods)
if (currentMods.Contains(s))
allOk = CheckAndInstallPackages(s);
if (!allOk) return;
Process p = new Process();
p.StartInfo.FileName = "OpenRA.Game.exe";
p.StartInfo.Arguments = "Game.Mods=" + string.Join(",", currentMods);
p.Start();
}
bool CheckAndInstallPackages(string mod)
{
string packageDir = "mods" + Path.DirectorySeparatorChar + mod + Path.DirectorySeparatorChar + "packages";
if (Directory.Exists(packageDir) &&
Directory.GetFiles(packageDir, "*.mix").Length > 0) return true;
var dialog = new InstallPackagesDialog(mod);
if (dialog.ShowDialog() != DialogResult.OK) return false;
return true;
}
}
}

View File

@@ -1,277 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="pictureBox1.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
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
</value>
</data>
</root>

27
OpenRA.Launcher/Mod.cs Normal file
View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OpenRA.Launcher
{
public class Mod
{
public string Title { get; private set; }
public string Version { get; private set; }
public string Author { get; private set; }
public string Description { get; private set; }
public string Requires { get; private set; }
public bool Standalone { get; private set; }
public Mod(string title, string version, string author, string description, string requires, bool standalone)
{
Title = title;
Version = version;
Author = author;
Description = description;
Requires = requires;
Standalone = standalone;
}
}
}

View File

@@ -42,11 +42,12 @@
</Target>
-->
<ItemGroup>
<Compile Include="ConfigureModsDialog.cs">
<Compile Include="JSBridge.cs" />
<Compile Include="Launcher.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="ConfigureModsDialog.Designer.cs">
<DependentUpon>ConfigureModsDialog.cs</DependentUpon>
<Compile Include="Launcher.Designer.cs">
<DependentUpon>Launcher.cs</DependentUpon>
</Compile>
<Compile Include="InstallPackagesDialog.cs">
<SubType>Form</SubType>
@@ -54,12 +55,7 @@
<Compile Include="InstallPackagesDialog.Designer.cs">
<DependentUpon>InstallPackagesDialog.cs</DependentUpon>
</Compile>
<Compile Include="MainForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="MainForm.Designer.cs">
<DependentUpon>MainForm.cs</DependentUpon>
</Compile>
<Compile Include="Mod.cs" />
<Compile Include="Program.cs" />
<Compile Include="Util.cs" />
<Compile Include="UtilityProgram.cs" />
@@ -75,15 +71,12 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="ConfigureModsDialog.resx">
<DependentUpon>ConfigureModsDialog.cs</DependentUpon>
<EmbeddedResource Include="Launcher.resx">
<DependentUpon>Launcher.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="InstallPackagesDialog.resx">
<DependentUpon>InstallPackagesDialog.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="MainForm.resx">
<DependentUpon>MainForm.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />

View File

@@ -10,7 +10,7 @@ namespace OpenRA.Launcher
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
Application.Run(new Launcher());
}
}
}