From d2a52fd529159745e730426890d92d7a70f719e4 Mon Sep 17 00:00:00 2001 From: Matthew Bowra-Dean Date: Thu, 18 Nov 2010 23:41:13 +1300 Subject: [PATCH] Bring Windows launcher into line with pchote's ideas. --- .../ConfigureModsDialog.Designer.cs | 233 -------------- OpenRA.Launcher/ConfigureModsDialog.cs | 291 ------------------ OpenRA.Launcher/JSBridge.cs | 47 +++ OpenRA.Launcher/Launcher.Designer.cs | 146 +++++++++ OpenRA.Launcher/Launcher.cs | 181 +++++++++++ ...ConfigureModsDialog.resx => Launcher.resx} | 0 OpenRA.Launcher/MainForm.Designer.cs | 160 ---------- OpenRA.Launcher/MainForm.cs | 82 ----- OpenRA.Launcher/MainForm.resx | 277 ----------------- OpenRA.Launcher/Mod.cs | 27 ++ OpenRA.Launcher/OpenRA.Launcher.csproj | 21 +- OpenRA.Launcher/Program.cs | 2 +- soviet-logo.png | Bin 8565 -> 4729 bytes 13 files changed, 409 insertions(+), 1058 deletions(-) delete mode 100644 OpenRA.Launcher/ConfigureModsDialog.Designer.cs delete mode 100644 OpenRA.Launcher/ConfigureModsDialog.cs create mode 100644 OpenRA.Launcher/JSBridge.cs create mode 100644 OpenRA.Launcher/Launcher.Designer.cs create mode 100644 OpenRA.Launcher/Launcher.cs rename OpenRA.Launcher/{ConfigureModsDialog.resx => Launcher.resx} (100%) delete mode 100644 OpenRA.Launcher/MainForm.Designer.cs delete mode 100644 OpenRA.Launcher/MainForm.cs delete mode 100644 OpenRA.Launcher/MainForm.resx create mode 100644 OpenRA.Launcher/Mod.cs diff --git a/OpenRA.Launcher/ConfigureModsDialog.Designer.cs b/OpenRA.Launcher/ConfigureModsDialog.Designer.cs deleted file mode 100644 index 37aa1c4f1e..0000000000 --- a/OpenRA.Launcher/ConfigureModsDialog.Designer.cs +++ /dev/null @@ -1,233 +0,0 @@ -namespace OpenRA.Launcher -{ - partial class ConfigureModsDialog - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - 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; - } -} \ No newline at end of file diff --git a/OpenRA.Launcher/ConfigureModsDialog.cs b/OpenRA.Launcher/ConfigureModsDialog.cs deleted file mode 100644 index ee7fe062b1..0000000000 --- a/OpenRA.Launcher/ConfigureModsDialog.cs +++ /dev/null @@ -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 activeMods; - - public List ActiveMods - { - get { return activeMods; } - } - - Dictionary allMods; - public ConfigureModsDialog(string[] activeMods) - { - InitializeComponent(); - - Util.UacShield(installButton); - - this.activeMods = new List(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 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 toRemove = new List(); - 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(""); - n.ForeColor = SystemColors.GrayText; - var m = new TreeNode(""); - 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 toAdd = new Stack(); - 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 toRemove = new List(); - - Stack nodes = new Stack(); - 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().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; - } - } -} diff --git a/OpenRA.Launcher/JSBridge.cs b/OpenRA.Launcher/JSBridge.cs new file mode 100644 index 0000000000..930f39237f --- /dev/null +++ b/OpenRA.Launcher/JSBridge.cs @@ -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 allMods; + + public JSBridge(Dictionary 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 modList = new List(); + 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(); + } + } +} diff --git a/OpenRA.Launcher/Launcher.Designer.cs b/OpenRA.Launcher/Launcher.Designer.cs new file mode 100644 index 0000000000..8f85fdd7a4 --- /dev/null +++ b/OpenRA.Launcher/Launcher.Designer.cs @@ -0,0 +1,146 @@ +namespace OpenRA.Launcher +{ + partial class Launcher + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + 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; + } +} \ No newline at end of file diff --git a/OpenRA.Launcher/Launcher.cs b/OpenRA.Launcher/Launcher.cs new file mode 100644 index 0000000000..4e42c954b9 --- /dev/null +++ b/OpenRA.Launcher/Launcher.cs @@ -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 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 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 remaining = new Stack(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("") { ForeColor = SystemColors.GrayText }; + var missing = new TreeNode("") { 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)); + } + } +} diff --git a/OpenRA.Launcher/ConfigureModsDialog.resx b/OpenRA.Launcher/Launcher.resx similarity index 100% rename from OpenRA.Launcher/ConfigureModsDialog.resx rename to OpenRA.Launcher/Launcher.resx diff --git a/OpenRA.Launcher/MainForm.Designer.cs b/OpenRA.Launcher/MainForm.Designer.cs deleted file mode 100644 index e45ec296c1..0000000000 --- a/OpenRA.Launcher/MainForm.Designer.cs +++ /dev/null @@ -1,160 +0,0 @@ -namespace OpenRA.Launcher -{ - partial class MainForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - 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; - } -} \ No newline at end of file diff --git a/OpenRA.Launcher/MainForm.cs b/OpenRA.Launcher/MainForm.cs deleted file mode 100644 index a7f3206c4f..0000000000 --- a/OpenRA.Launcher/MainForm.cs +++ /dev/null @@ -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; - } - } -} diff --git a/OpenRA.Launcher/MainForm.resx b/OpenRA.Launcher/MainForm.resx deleted file mode 100644 index 9383875582..0000000000 --- a/OpenRA.Launcher/MainForm.resx +++ /dev/null @@ -1,277 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - - 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 - - - diff --git a/OpenRA.Launcher/Mod.cs b/OpenRA.Launcher/Mod.cs new file mode 100644 index 0000000000..3815ab1a10 --- /dev/null +++ b/OpenRA.Launcher/Mod.cs @@ -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; + } + } +} diff --git a/OpenRA.Launcher/OpenRA.Launcher.csproj b/OpenRA.Launcher/OpenRA.Launcher.csproj index 5a3b1b29bf..a16b8aea40 100644 --- a/OpenRA.Launcher/OpenRA.Launcher.csproj +++ b/OpenRA.Launcher/OpenRA.Launcher.csproj @@ -42,11 +42,12 @@ --> - + + Form - - ConfigureModsDialog.cs + + Launcher.cs Form @@ -54,12 +55,7 @@ InstallPackagesDialog.cs - - Form - - - MainForm.cs - + @@ -75,15 +71,12 @@ - - ConfigureModsDialog.cs + + Launcher.cs InstallPackagesDialog.cs - - MainForm.cs - diff --git a/OpenRA.Launcher/Program.cs b/OpenRA.Launcher/Program.cs index ad9104302d..79ad06b49d 100644 --- a/OpenRA.Launcher/Program.cs +++ b/OpenRA.Launcher/Program.cs @@ -10,7 +10,7 @@ namespace OpenRA.Launcher { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new MainForm()); + Application.Run(new Launcher()); } } } diff --git a/soviet-logo.png b/soviet-logo.png index b858fd6816756683c2d7e9880eb16a67366fa789..96f770fa73a93151a29489f8a25af4befe7d69e8 100644 GIT binary patch delta 2083 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