├── .gitignore
├── FlashpointInstaller
├── App.config
├── FlashpointInstaller.csproj
├── FlashpointInstaller.ico
├── FlashpointInstaller.sln
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── Resources
│ └── FlashpointLogo.png
├── packages.config
└── src
│ ├── Common.cs
│ ├── Forms
│ ├── Finish.Designer.cs
│ ├── Finish.cs
│ ├── Finish.resx
│ ├── Main.Designer.cs
│ ├── Main.cs
│ ├── Main.resx
│ ├── Operate.Designer.cs
│ ├── Operate.cs
│ └── Operate.resx
│ ├── Program.cs
│ └── TriStateTreeView.cs
├── FlashpointManager
├── App.config
├── FlashpointManager.csproj
├── FlashpointManager.ico
├── FlashpointManager.sln
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ ├── Settings.settings
│ └── app.manifest
├── packages.config
└── src
│ ├── Common.cs
│ ├── Forms
│ ├── CheckFiles.Designer.cs
│ ├── CheckFiles.cs
│ ├── CheckFiles.resx
│ ├── Main.Designer.cs
│ ├── Main.cs
│ ├── Main.resx
│ ├── Operate.Designer.cs
│ ├── Operate.cs
│ ├── Operate.resx
│ └── Settings.cs
│ ├── Program.cs
│ └── TriStateTreeView.cs
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs/
2 | bin/
3 | obj/
4 | packages/
5 | *.csproj.user
6 | FodyWeavers.xsd
7 | FodyWeavers.xml
--------------------------------------------------------------------------------
/FlashpointInstaller/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/FlashpointInstaller/FlashpointInstaller.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlashpointProject/FlashpointComponentTools/0628df6e079542f566c170ca16501e420ab04b2c/FlashpointInstaller/FlashpointInstaller.ico
--------------------------------------------------------------------------------
/FlashpointInstaller/FlashpointInstaller.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32616.157
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlashpointInstaller", "FlashpointInstaller.csproj", "{3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Debug|x86 = Debug|x86
12 | Release|Any CPU = Release|Any CPU
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Debug|x86.ActiveCfg = Debug|x86
19 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Debug|x86.Build.0 = Debug|x86
20 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Release|x86.ActiveCfg = Release|x86
23 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Release|x86.Build.0 = Release|x86
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {F7D16A14-B30F-464C-9278-7F0A094AD68F}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/FlashpointInstaller/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("Flashpoint Installer")]
8 | [assembly: AssemblyDescription("Installs Flashpoint and its components.")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("Flashpoint Installer")]
12 | [assembly: AssemblyCopyright("")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("3a52cec9-3eec-4ed4-8e21-51635a08ac38")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.3.0.0")]
35 | [assembly: AssemblyFileVersion("1.3")]
36 |
--------------------------------------------------------------------------------
/FlashpointInstaller/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace FlashpointInstaller.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FlashpointInstaller.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/FlashpointInstaller/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/FlashpointInstaller/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace FlashpointInstaller.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.2.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/FlashpointInstaller/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/FlashpointInstaller/Resources/FlashpointLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlashpointProject/FlashpointComponentTools/0628df6e079542f566c170ca16501e420ab04b2c/FlashpointInstaller/Resources/FlashpointLogo.png
--------------------------------------------------------------------------------
/FlashpointInstaller/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/FlashpointInstaller/src/Common.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Drawing;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Net;
8 | using System.Windows.Forms;
9 | using System.Xml;
10 |
11 | namespace FlashpointInstaller
12 | {
13 | namespace Common
14 | {
15 | // Component object definition
16 | public class Component : Category
17 | {
18 | public string URL { get; set; }
19 | public string Hash { get; set; }
20 | public long Size { get; set; }
21 | public string Path { get; set; }
22 | public string[] Depends { get; set; } = new string[] { };
23 |
24 | public Component(XmlNode node) : base(node)
25 | {
26 | // URL
27 |
28 | XmlNode rootElement = node.OwnerDocument.GetElementsByTagName("list")[0];
29 |
30 | if (rootElement.Attributes != null && rootElement.Attributes["url"] != null)
31 | {
32 | URL = rootElement.Attributes["url"].Value + ID + ".zip";
33 | }
34 | else
35 | {
36 | MessageBox.Show(
37 | "An error occurred while parsing the component list XML. Please alert Flashpoint staff ASAP!\n\n" +
38 | "Description: Root element does not contain URL attribute",
39 | "Error", MessageBoxButtons.OK, MessageBoxIcon.Error
40 | );
41 |
42 | Environment.Exit(1);
43 | }
44 |
45 | // Hash
46 |
47 | string hash = GetAttribute(node, "hash", true);
48 |
49 | if (hash.Length == 8)
50 | {
51 | Hash = hash;
52 | }
53 | else
54 | {
55 | MessageBox.Show(
56 | "An error occurred while parsing the component list XML. Please alert Flashpoint staff ASAP!\n\n" +
57 | $"Description: Hash of component \"{Title}\" is invalid",
58 | "Error", MessageBoxButtons.OK, MessageBoxIcon.Error
59 | );
60 |
61 | Environment.Exit(1);
62 | }
63 |
64 | // Size
65 |
66 | long size = 0;
67 | string stringSize = GetAttribute(node, "install-size", false);
68 |
69 | if (stringSize != "" && !long.TryParse(stringSize, out size))
70 | {
71 | MessageBox.Show(
72 | "An error occurred while parsing the component list XML. Please alert Flashpoint staff ASAP!\n\n" +
73 | $"Description: Size of component \"{Title}\" is not a number",
74 | "Error", MessageBoxButtons.OK, MessageBoxIcon.Error
75 | );
76 |
77 | Environment.Exit(1);
78 | }
79 |
80 | Size = size;
81 |
82 | // Path
83 |
84 | Path = GetAttribute(node, "path", false);
85 |
86 | // Depends
87 |
88 | string depends = GetAttribute(node, "depends", false);
89 |
90 | if (depends.Length > 0) Depends = depends.Split(' ');
91 | }
92 | }
93 |
94 | // Category object definition
95 | public class Category
96 | {
97 | public string Title { get; set; }
98 | public string Description { get; set; }
99 | public string ID { get; set; }
100 | public bool Required { get; set; }
101 |
102 | public Category(XmlNode node)
103 | {
104 | // ID
105 |
106 | XmlNode workingNode = node.ParentNode;
107 | string id = GetAttribute(node, "id", true);
108 |
109 | while (workingNode != null && workingNode.Name != "list")
110 | {
111 | if (workingNode.Attributes != null && workingNode.Name != "list")
112 | {
113 | id = $"{GetAttribute(workingNode, "id", true)}-{id}";
114 | }
115 |
116 | workingNode = workingNode.ParentNode;
117 | }
118 |
119 | ID = id;
120 |
121 | // Everything else
122 |
123 | Title = GetAttribute(node, "title", true);
124 | Description = GetAttribute(node, "description", true);
125 | Required = ID.Split('-').FirstOrDefault() == "core";
126 | }
127 |
128 | protected static string GetAttribute(XmlNode node, string attribute, bool throwError)
129 | {
130 | if (node.Attributes != null && node.Attributes[attribute] != null)
131 | {
132 | return node.Attributes[attribute].Value;
133 | }
134 | else if (throwError)
135 | {
136 | MessageBox.Show(
137 | "An error occurred while parsing the component list XML. Please alert Flashpoint staff ASAP!\n\n" +
138 | $"Description: Required {node.Name} attribute \"{attribute}\" was not found",
139 | "Error", MessageBoxButtons.OK, MessageBoxIcon.Error
140 | );
141 |
142 | Environment.Exit(1);
143 | }
144 |
145 | return "";
146 | }
147 | }
148 |
149 | public static class FPM
150 | {
151 | // Pointer to main form
152 | public static Main Main { get => (Main)Application.OpenForms["Main"]; }
153 | // Internet location of component list XML
154 | public static string ListURL { get; set; } = "https://nexus-dev.flashpointarchive.org/repository/stable/components.xml";
155 | // The parsed component list XML
156 | public static XmlDocument XmlTree { get; set; }
157 | // WebClient instance used to download files
158 | public static _WebClient Client { get; } = new _WebClient();
159 |
160 | // Check if Visual C++ 2015 x86 redistributable is installed
161 | public static bool RedistInstalled
162 | {
163 | get => Registry.LocalMachine.OpenSubKey("SOFTWARE\\WOW6432Node\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\X86") != null;
164 | }
165 |
166 | // Performs an operation on every node in the specified TreeNodeCollection
167 | public static void IterateList(TreeNodeCollection parent, Action action)
168 | {
169 | foreach (TreeNode childNode in parent)
170 | {
171 | action(childNode);
172 |
173 | IterateList(childNode.Nodes, action);
174 | }
175 | }
176 |
177 | // Performs an operation on every node in the specified XmlNodeList
178 | public static void IterateXML(XmlNodeList parent, Action action)
179 | {
180 | foreach (XmlNode childNode in parent)
181 | {
182 | action(childNode);
183 |
184 | IterateXML(childNode.ChildNodes, action);
185 | }
186 | }
187 |
188 | // Calls the AddNodeToList method on every child of the specified XML node
189 | public static void RecursiveAddToList(XmlNode sourceNode, TreeNodeCollection destNode)
190 | {
191 | foreach (XmlNode node in sourceNode.ChildNodes)
192 | {
193 | var listNode = AddNodeToList(node, destNode);
194 |
195 | RecursiveAddToList(node, listNode.Nodes);
196 | }
197 | }
198 |
199 | // Formats an XML node as a TreeView node and adds it to the specified TreeView
200 | public static TreeNode AddNodeToList(XmlNode child, TreeNodeCollection parent)
201 | {
202 | TreeNode listNode = new TreeNode();
203 |
204 | // Add properties to TreeNode based on the XML element
205 | // (I can use the dynamic type to prevent redundancy, but I noticed it makes the application load significantly slower)
206 | if (child.Name == "component")
207 | {
208 | var component = new Component(child);
209 |
210 | listNode.Text = component.Title;
211 | listNode.Name = component.ID;
212 |
213 | if (component.Required)
214 | {
215 | listNode.ForeColor = Color.FromArgb(255, 96, 96, 96);
216 | }
217 |
218 | listNode.Tag = component;
219 | }
220 | else if (child.Name == "category")
221 | {
222 | var category = new Category(child);
223 |
224 | listNode.Text = category.Title;
225 | listNode.Name = category.ID;
226 |
227 | if (category.Required)
228 | {
229 | listNode.ForeColor = Color.FromArgb(255, 96, 96, 96);
230 | }
231 |
232 | listNode.Tag = category;
233 | }
234 |
235 | parent.Add(listNode);
236 |
237 | // Initialize checkbox
238 | // (the Checked attribute needs to be explicitly set or else the checkbox won't appear)
239 | listNode.Checked = child.Name == "component" && !listNode.Name.StartsWith("extra");
240 |
241 | return listNode;
242 | }
243 |
244 | // Checks if specified Flashpoint destination path is valid
245 | public static bool VerifyDestinationPath(string path)
246 | {
247 | bool alreadyExists = File.Exists(Path.Combine(path, "Launcher", "Flashpoint.exe"));
248 |
249 | IterateXML(XmlTree.GetElementsByTagName("list")[0].ChildNodes, node =>
250 | {
251 | if (alreadyExists || node.Name != "component") return;
252 |
253 | if (File.Exists(Path.Combine(path, "Components", new Component(node).ID)))
254 | {
255 | alreadyExists = true;
256 | }
257 | });
258 |
259 | if (alreadyExists)
260 | {
261 | MessageBox.Show(
262 | "Flashpoint is already installed to this directory! " +
263 | "Choose a different folder or uninstall the existing copy first.",
264 | "Error", MessageBoxButtons.OK, MessageBoxIcon.Error
265 | );
266 |
267 | return false;
268 | }
269 |
270 | if (!Path.IsPathRooted(path))
271 | {
272 | MessageBox.Show(
273 | "The specified directory is not valid! Choose a different folder.",
274 | "Error", MessageBoxButtons.OK, MessageBoxIcon.Error
275 | );
276 |
277 | return false;
278 | }
279 |
280 | string errorPath;
281 |
282 | if (path.StartsWith(Environment.ExpandEnvironmentVariables("%ProgramW6432%"))
283 | || path.StartsWith(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)))
284 | {
285 | errorPath = "Program Files";
286 | }
287 | else if (path.StartsWith(Path.GetTempPath().TrimEnd('\\')))
288 | {
289 | errorPath = "Temporary Files";
290 | }
291 | else if (path.StartsWith(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "OneDrive")))
292 | {
293 | errorPath = "OneDrive";
294 | }
295 | else
296 | {
297 | return true;
298 | }
299 |
300 | MessageBox.Show(
301 | $"Flashpoint cannot be installed to the {errorPath} directory! Choose a different folder.",
302 | "Error", MessageBoxButtons.OK, MessageBoxIcon.Error
303 | );
304 |
305 | return false;
306 | }
307 |
308 | // Checks if any dependencies were not marked for download by the user, and marks them accordingly
309 | public static bool CheckDependencies()
310 | {
311 | List requiredDepends = new List();
312 | List missingDepends = new List();
313 |
314 | void AddDependencies(string[] depends)
315 | {
316 | requiredDepends.AddRange(depends);
317 |
318 | foreach (string depend in depends)
319 | {
320 | var query = Main.ComponentList.Nodes.Find(depend, true);
321 |
322 | if (query.Length > 0) AddDependencies((query[0].Tag as Component).Depends);
323 | }
324 | }
325 |
326 | // First, fill out a list of dependencies
327 | IterateList(Main.ComponentList.Nodes, node =>
328 | {
329 | if (node.Checked && node.Tag.GetType().ToString().EndsWith("Component"))
330 | {
331 | AddDependencies((node.Tag as Component).Depends);
332 | }
333 | });
334 |
335 | // Then make sure they're all marked accordingly
336 | IterateList(Main.ComponentList.Nodes, node =>
337 | {
338 | if (node.Tag.GetType().ToString().EndsWith("Component"))
339 | {
340 | var component = node.Tag as Component;
341 |
342 | if (requiredDepends.Any(depend => depend == component.ID) && !node.Checked)
343 | {
344 | missingDepends.Add(node);
345 | }
346 | }
347 | });
348 |
349 | if (missingDepends.Count > 0)
350 | {
351 | long missingSize = missingDepends.Select(n => (n.Tag as Component).Size).Sum();
352 |
353 | var result = MessageBox.Show(
354 | "The following dependencies will also be installed:\n\n" +
355 | string.Join(", ", missingDepends.Select(n => (n.Tag as Component).Title)) + "\n\n" +
356 | $"This adds an additional {GetFormattedBytes(missingSize)} to your download. Is this OK?",
357 | "Notice", MessageBoxButtons.YesNo, MessageBoxIcon.Information
358 | );
359 |
360 | if (result == DialogResult.Yes)
361 | {
362 | missingDepends.ForEach(d => d.Checked = true);
363 | }
364 | else
365 | {
366 | return false;
367 | }
368 | }
369 |
370 | return true;
371 | }
372 |
373 | // Formats bytes as a human-readable string
374 | public static string GetFormattedBytes(long bytes)
375 | {
376 | string[] units = new[] { " bytes", "KB", "MB", "GB" };
377 | int i = units.Length;
378 |
379 | while (--i >= 0)
380 | {
381 | double unitSize = Math.Pow(1024, i);
382 | if (bytes >= unitSize) return (Math.Round(bytes / unitSize * 10) / 10).ToString("N1") + units[i];
383 | }
384 |
385 | return "0 bytes";
386 | }
387 | }
388 | }
389 |
390 | // Modified WebClient class with shortened timeout
391 | public class _WebClient : WebClient
392 | {
393 | protected override WebRequest GetWebRequest(Uri address)
394 | {
395 | var request = base.GetWebRequest(address);
396 | request.Timeout = 3000;
397 | return request;
398 | }
399 | }
400 | }
--------------------------------------------------------------------------------
/FlashpointInstaller/src/Forms/Finish.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace FlashpointInstaller
2 | {
3 | partial class Finish
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.Message = new System.Windows.Forms.Label();
32 | this.RunOnClose = new System.Windows.Forms.CheckBox();
33 | this.CloseButton = new System.Windows.Forms.Button();
34 | this.SuspendLayout();
35 | //
36 | // Message
37 | //
38 | this.Message.Location = new System.Drawing.Point(12, 10);
39 | this.Message.Name = "Message";
40 | this.Message.Size = new System.Drawing.Size(280, 20);
41 | this.Message.TabIndex = 0;
42 | this.Message.Text = "Flashpoint has been successfully installed.";
43 | this.Message.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
44 | //
45 | // RunOnClose
46 | //
47 | this.RunOnClose.AutoSize = true;
48 | this.RunOnClose.Checked = true;
49 | this.RunOnClose.CheckState = System.Windows.Forms.CheckState.Checked;
50 | this.RunOnClose.Location = new System.Drawing.Point(12, 45);
51 | this.RunOnClose.Name = "RunOnClose";
52 | this.RunOnClose.Size = new System.Drawing.Size(113, 17);
53 | this.RunOnClose.TabIndex = 1;
54 | this.RunOnClose.Text = "&Launch Flashpoint";
55 | this.RunOnClose.UseVisualStyleBackColor = true;
56 | //
57 | // CloseButton
58 | //
59 | this.CloseButton.Location = new System.Drawing.Point(218, 42);
60 | this.CloseButton.Name = "CloseButton";
61 | this.CloseButton.Size = new System.Drawing.Size(75, 22);
62 | this.CloseButton.TabIndex = 2;
63 | this.CloseButton.Text = "&Close";
64 | this.CloseButton.UseVisualStyleBackColor = true;
65 | this.CloseButton.Click += new System.EventHandler(this.FinishDownload_Exit);
66 | //
67 | // Finish
68 | //
69 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
70 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
71 | this.ClientSize = new System.Drawing.Size(304, 73);
72 | this.Controls.Add(this.CloseButton);
73 | this.Controls.Add(this.RunOnClose);
74 | this.Controls.Add(this.Message);
75 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
76 | this.MaximizeBox = false;
77 | this.MinimizeBox = false;
78 | this.Name = "Finish";
79 | this.ShowIcon = false;
80 | this.ShowInTaskbar = false;
81 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
82 | this.Text = "Installation Complete";
83 | this.TopMost = true;
84 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FinishDownload_Exit);
85 | this.ResumeLayout(false);
86 | this.PerformLayout();
87 |
88 | }
89 |
90 | #endregion
91 |
92 | private System.Windows.Forms.Label Message;
93 | private System.Windows.Forms.CheckBox RunOnClose;
94 | private System.Windows.Forms.Button CloseButton;
95 | }
96 | }
--------------------------------------------------------------------------------
/FlashpointInstaller/src/Forms/Finish.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Windows.Forms;
5 |
6 | using FlashpointInstaller.Common;
7 |
8 | namespace FlashpointInstaller
9 | {
10 | public partial class Finish : Form
11 | {
12 | public Finish() => InitializeComponent();
13 |
14 | private void FinishDownload_Exit(object sender, EventArgs e)
15 | {
16 | if (RunOnClose.Checked)
17 | {
18 | new Process() { StartInfo = {
19 | UseShellExecute = true,
20 | FileName = "Flashpoint.exe",
21 | WorkingDirectory = Path.Combine(FPM.Main.DestinationPath.Text, "Launcher")
22 | }}.Start();
23 | }
24 |
25 | Environment.Exit(0);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/FlashpointInstaller/src/Forms/Finish.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/FlashpointInstaller/src/Forms/Main.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace FlashpointInstaller
2 | {
3 | partial class Main
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.components = new System.ComponentModel.Container();
32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Main));
33 | this.Logo = new System.Windows.Forms.PictureBox();
34 | this.About = new System.Windows.Forms.Label();
35 | this.DestinationPathBox = new System.Windows.Forms.GroupBox();
36 | this.DestinationPath = new System.Windows.Forms.TextBox();
37 | this.DestinationPathBrowse = new System.Windows.Forms.Button();
38 | this.InstallButton = new System.Windows.Forms.Button();
39 | this.Link = new System.Windows.Forms.LinkLabel();
40 | this.Message2 = new System.Windows.Forms.Label();
41 | this.DescriptionBox = new System.Windows.Forms.GroupBox();
42 | this.Description = new System.Windows.Forms.Label();
43 | this.ShortcutTable = new System.Windows.Forms.TableLayoutPanel();
44 | this.ShortcutStartMenu = new System.Windows.Forms.CheckBox();
45 | this.ShortcutDesktop = new System.Windows.Forms.CheckBox();
46 | this.ShortcutLabel = new System.Windows.Forms.Label();
47 | this.Message = new System.Windows.Forms.Label();
48 | this.ComponentList = new RikTheVeggie.TriStateTreeView();
49 | ((System.ComponentModel.ISupportInitialize)(this.Logo)).BeginInit();
50 | this.DestinationPathBox.SuspendLayout();
51 | this.DescriptionBox.SuspendLayout();
52 | this.ShortcutTable.SuspendLayout();
53 | this.SuspendLayout();
54 | //
55 | // Logo
56 | //
57 | this.Logo.Image = ((System.Drawing.Image)(resources.GetObject("Logo.Image")));
58 | this.Logo.Location = new System.Drawing.Point(32, 18);
59 | this.Logo.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
60 | this.Logo.Name = "Logo";
61 | this.Logo.Size = new System.Drawing.Size(886, 272);
62 | this.Logo.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage;
63 | this.Logo.TabIndex = 0;
64 | this.Logo.TabStop = false;
65 | //
66 | // About
67 | //
68 | this.About.AutoSize = true;
69 | this.About.Location = new System.Drawing.Point(27, 295);
70 | this.About.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
71 | this.About.Name = "About";
72 | this.About.Size = new System.Drawing.Size(143, 20);
73 | this.About.TabIndex = 0;
74 | this.About.Text = "Flashpoint Installer";
75 | //
76 | // DestinationPathBox
77 | //
78 | this.DestinationPathBox.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
79 | this.DestinationPathBox.Controls.Add(this.DestinationPath);
80 | this.DestinationPathBox.Controls.Add(this.DestinationPathBrowse);
81 | this.DestinationPathBox.Location = new System.Drawing.Point(18, 591);
82 | this.DestinationPathBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
83 | this.DestinationPathBox.Name = "DestinationPathBox";
84 | this.DestinationPathBox.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5);
85 | this.DestinationPathBox.Size = new System.Drawing.Size(900, 75);
86 | this.DestinationPathBox.TabIndex = 6;
87 | this.DestinationPathBox.TabStop = false;
88 | this.DestinationPathBox.Text = "Destination Folder";
89 | //
90 | // DestinationPath
91 | //
92 | this.DestinationPath.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
93 | this.DestinationPath.Location = new System.Drawing.Point(14, 28);
94 | this.DestinationPath.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
95 | this.DestinationPath.Name = "DestinationPath";
96 | this.DestinationPath.Size = new System.Drawing.Size(752, 26);
97 | this.DestinationPath.TabIndex = 0;
98 | this.DestinationPath.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.DestinationPath_KeyPress);
99 | //
100 | // DestinationPathBrowse
101 | //
102 | this.DestinationPathBrowse.Location = new System.Drawing.Point(778, 26);
103 | this.DestinationPathBrowse.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
104 | this.DestinationPathBrowse.Name = "DestinationPathBrowse";
105 | this.DestinationPathBrowse.Size = new System.Drawing.Size(110, 34);
106 | this.DestinationPathBrowse.TabIndex = 1;
107 | this.DestinationPathBrowse.Text = "&Browse";
108 | this.DestinationPathBrowse.UseVisualStyleBackColor = true;
109 | this.DestinationPathBrowse.Click += new System.EventHandler(this.DestinationPathBrowse_Click);
110 | //
111 | // InstallButton
112 | //
113 | this.InstallButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
114 | this.InstallButton.Location = new System.Drawing.Point(450, 682);
115 | this.InstallButton.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
116 | this.InstallButton.Name = "InstallButton";
117 | this.InstallButton.Size = new System.Drawing.Size(470, 40);
118 | this.InstallButton.TabIndex = 8;
119 | this.InstallButton.Text = "&Install Flashpoint";
120 | this.InstallButton.UseVisualStyleBackColor = true;
121 | this.InstallButton.Click += new System.EventHandler(this.InstallButton_Click);
122 | //
123 | // Link
124 | //
125 | this.Link.AutoSize = true;
126 | this.Link.LinkColor = System.Drawing.Color.FromArgb(((int)(((byte)(4)))), ((int)(((byte)(94)))), ((int)(((byte)(221)))));
127 | this.Link.Location = new System.Drawing.Point(713, 295);
128 | this.Link.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
129 | this.Link.Name = "Link";
130 | this.Link.Size = new System.Drawing.Size(207, 20);
131 | this.Link.TabIndex = 1;
132 | this.Link.TabStop = true;
133 | this.Link.Text = "https://flashpointarchive.org/";
134 | this.Link.TextAlign = System.Drawing.ContentAlignment.TopRight;
135 | this.Link.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.Link_LinkClicked);
136 | //
137 | // Message2
138 | //
139 | this.Message2.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
140 | this.Message2.Location = new System.Drawing.Point(622, 395);
141 | this.Message2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
142 | this.Message2.Name = "Message2";
143 | this.Message2.Size = new System.Drawing.Size(296, 46);
144 | this.Message2.TabIndex = 4;
145 | this.Message2.Text = "Click on a component or category to learn more about it.";
146 | //
147 | // DescriptionBox
148 | //
149 | this.DescriptionBox.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
150 | this.DescriptionBox.Controls.Add(this.Description);
151 | this.DescriptionBox.Location = new System.Drawing.Point(622, 469);
152 | this.DescriptionBox.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
153 | this.DescriptionBox.Name = "DescriptionBox";
154 | this.DescriptionBox.Padding = new System.Windows.Forms.Padding(15, 12, 15, 15);
155 | this.DescriptionBox.Size = new System.Drawing.Size(296, 111);
156 | this.DescriptionBox.TabIndex = 5;
157 | this.DescriptionBox.TabStop = false;
158 | this.DescriptionBox.Text = "Component Description";
159 | this.DescriptionBox.Visible = false;
160 | //
161 | // Description
162 | //
163 | this.Description.Location = new System.Drawing.Point(15, 32);
164 | this.Description.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
165 | this.Description.Name = "Description";
166 | this.Description.Size = new System.Drawing.Size(266, 63);
167 | this.Description.TabIndex = 0;
168 | //
169 | // ShortcutTable
170 | //
171 | this.ShortcutTable.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
172 | this.ShortcutTable.ColumnCount = 3;
173 | this.ShortcutTable.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
174 | this.ShortcutTable.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
175 | this.ShortcutTable.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
176 | this.ShortcutTable.Controls.Add(this.ShortcutStartMenu, 2, 0);
177 | this.ShortcutTable.Controls.Add(this.ShortcutDesktop, 1, 0);
178 | this.ShortcutTable.Controls.Add(this.ShortcutLabel, 0, 0);
179 | this.ShortcutTable.Location = new System.Drawing.Point(18, 680);
180 | this.ShortcutTable.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
181 | this.ShortcutTable.Name = "ShortcutTable";
182 | this.ShortcutTable.RowCount = 1;
183 | this.ShortcutTable.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
184 | this.ShortcutTable.Size = new System.Drawing.Size(411, 40);
185 | this.ShortcutTable.TabIndex = 7;
186 | //
187 | // ShortcutStartMenu
188 | //
189 | this.ShortcutStartMenu.AutoSize = true;
190 | this.ShortcutStartMenu.Checked = true;
191 | this.ShortcutStartMenu.CheckState = System.Windows.Forms.CheckState.Checked;
192 | this.ShortcutStartMenu.Location = new System.Drawing.Point(293, 5);
193 | this.ShortcutStartMenu.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
194 | this.ShortcutStartMenu.Name = "ShortcutStartMenu";
195 | this.ShortcutStartMenu.Padding = new System.Windows.Forms.Padding(0, 5, 0, 0);
196 | this.ShortcutStartMenu.Size = new System.Drawing.Size(114, 29);
197 | this.ShortcutStartMenu.TabIndex = 2;
198 | this.ShortcutStartMenu.Text = "&Start Menu";
199 | this.ShortcutStartMenu.UseVisualStyleBackColor = true;
200 | //
201 | // ShortcutDesktop
202 | //
203 | this.ShortcutDesktop.AutoSize = true;
204 | this.ShortcutDesktop.Checked = true;
205 | this.ShortcutDesktop.CheckState = System.Windows.Forms.CheckState.Checked;
206 | this.ShortcutDesktop.Location = new System.Drawing.Point(190, 5);
207 | this.ShortcutDesktop.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
208 | this.ShortcutDesktop.Name = "ShortcutDesktop";
209 | this.ShortcutDesktop.Padding = new System.Windows.Forms.Padding(0, 5, 0, 0);
210 | this.ShortcutDesktop.Size = new System.Drawing.Size(95, 29);
211 | this.ShortcutDesktop.TabIndex = 1;
212 | this.ShortcutDesktop.Text = "&Desktop";
213 | this.ShortcutDesktop.UseVisualStyleBackColor = true;
214 | //
215 | // ShortcutLabel
216 | //
217 | this.ShortcutLabel.AutoSize = true;
218 | this.ShortcutLabel.Dock = System.Windows.Forms.DockStyle.Fill;
219 | this.ShortcutLabel.Location = new System.Drawing.Point(4, 5);
220 | this.ShortcutLabel.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
221 | this.ShortcutLabel.Name = "ShortcutLabel";
222 | this.ShortcutLabel.Size = new System.Drawing.Size(178, 30);
223 | this.ShortcutLabel.TabIndex = 0;
224 | this.ShortcutLabel.Text = "Create shortcuts in:";
225 | this.ShortcutLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
226 | //
227 | // Message
228 | //
229 | this.Message.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
230 | this.Message.Location = new System.Drawing.Point(622, 346);
231 | this.Message.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
232 | this.Message.Name = "Message";
233 | this.Message.Size = new System.Drawing.Size(296, 46);
234 | this.Message.TabIndex = 3;
235 | this.Message.Text = "Choose components to include in your Flashpoint installation.";
236 | //
237 | // ComponentList
238 | //
239 | this.ComponentList.Anchor = System.Windows.Forms.AnchorStyles.Bottom;
240 | this.ComponentList.Indent = 20;
241 | this.ComponentList.Location = new System.Drawing.Point(32, 338);
242 | this.ComponentList.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
243 | this.ComponentList.Name = "ComponentList";
244 | this.ComponentList.Size = new System.Drawing.Size(574, 238);
245 | this.ComponentList.TabIndex = 2;
246 | this.ComponentList.TriStateStyleProperty = RikTheVeggie.TriStateTreeView.TriStateStyles.Installer;
247 | this.ComponentList.BeforeCheck += new System.Windows.Forms.TreeViewCancelEventHandler(this.ComponentList_BeforeCheck);
248 | this.ComponentList.AfterCheck += new System.Windows.Forms.TreeViewEventHandler(this.ComponentList_AfterCheck);
249 | this.ComponentList.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.ComponentList_BeforeSelect);
250 | //
251 | // Main
252 | //
253 | this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
254 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
255 | this.ClientSize = new System.Drawing.Size(936, 740);
256 | this.Controls.Add(this.DescriptionBox);
257 | this.Controls.Add(this.ShortcutTable);
258 | this.Controls.Add(this.Message);
259 | this.Controls.Add(this.ComponentList);
260 | this.Controls.Add(this.DestinationPathBox);
261 | this.Controls.Add(this.InstallButton);
262 | this.Controls.Add(this.Link);
263 | this.Controls.Add(this.Message2);
264 | this.Controls.Add(this.About);
265 | this.Controls.Add(this.Logo);
266 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
267 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
268 | this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
269 | this.MaximizeBox = false;
270 | this.Name = "Main";
271 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
272 | this.Text = "Flashpoint Installer";
273 | this.Load += new System.EventHandler(this.Main_Load);
274 | ((System.ComponentModel.ISupportInitialize)(this.Logo)).EndInit();
275 | this.DestinationPathBox.ResumeLayout(false);
276 | this.DestinationPathBox.PerformLayout();
277 | this.DescriptionBox.ResumeLayout(false);
278 | this.ShortcutTable.ResumeLayout(false);
279 | this.ShortcutTable.PerformLayout();
280 | this.ResumeLayout(false);
281 | this.PerformLayout();
282 |
283 | }
284 |
285 | #endregion
286 |
287 | private System.Windows.Forms.PictureBox Logo;
288 | private System.Windows.Forms.Label About;
289 | private System.Windows.Forms.GroupBox DestinationPathBox;
290 | public System.Windows.Forms.TextBox DestinationPath;
291 | private System.Windows.Forms.Button DestinationPathBrowse;
292 | private System.Windows.Forms.LinkLabel Link;
293 | private System.Windows.Forms.Label Message;
294 | public RikTheVeggie.TriStateTreeView ComponentList;
295 | private System.Windows.Forms.TableLayoutPanel ShortcutTable;
296 | private System.Windows.Forms.Label ShortcutLabel;
297 | public System.Windows.Forms.CheckBox ShortcutStartMenu;
298 | public System.Windows.Forms.CheckBox ShortcutDesktop;
299 | private System.Windows.Forms.GroupBox DescriptionBox;
300 | private System.Windows.Forms.Label Description;
301 | private System.Windows.Forms.Label Message2;
302 | public System.Windows.Forms.Button InstallButton;
303 | }
304 | }
305 |
306 |
--------------------------------------------------------------------------------
/FlashpointInstaller/src/Forms/Main.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Windows.Forms;
6 | using System.Xml;
7 |
8 | using FlashpointInstaller.Common;
9 | using Microsoft.WindowsAPICodePack.Dialogs;
10 |
11 | namespace FlashpointInstaller
12 | {
13 | public partial class Main : Form
14 | {
15 | public Main() => InitializeComponent();
16 |
17 | private void Main_Load(object sender, EventArgs e)
18 | {
19 | About.Text += $" v{Application.ProductVersion}";
20 |
21 | XmlNodeList rootElements = FPM.XmlTree.GetElementsByTagName("list");
22 |
23 | if (rootElements.Count > 0)
24 | {
25 | FPM.RecursiveAddToList(rootElements[0], ComponentList.Nodes);
26 | }
27 | else
28 | {
29 | MessageBox.Show(
30 | "An error occurred while parsing the component list XML. Please alert Flashpoint staff ASAP!\n\n" +
31 | "Description: Root element was not found",
32 | "Error", MessageBoxButtons.OK, MessageBoxIcon.Error
33 | );
34 |
35 | Environment.Exit(1);
36 | }
37 |
38 | DestinationPath.Text = Path.Combine(Path.GetPathRoot(AppDomain.CurrentDomain.BaseDirectory), "Flashpoint");
39 | }
40 |
41 | private void Link_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
42 | {
43 | Process.Start(new ProcessStartInfo("https://flashpointarchive.org/") { UseShellExecute = true });
44 | }
45 |
46 | public void ComponentList_BeforeCheck(object sender, TreeViewCancelEventArgs e)
47 | {
48 | bool required = e.Node.Tag.GetType().ToString().EndsWith("Component")
49 | ? (e.Node.Tag as Component).Required
50 | : (e.Node.Tag as Category).Required;
51 |
52 | if (required && e.Node.Checked) e.Cancel = true;
53 | }
54 |
55 | private void ComponentList_AfterCheck(object sender, TreeViewEventArgs e)
56 | {
57 | long size = 0;
58 |
59 | FPM.IterateList(ComponentList.Nodes, node =>
60 | {
61 | if (!node.Checked || !node.Tag.GetType().ToString().EndsWith("Component")) return;
62 |
63 | size += (node.Tag as Component).Size;
64 | });
65 |
66 | InstallButton.Text = $"Install Flashpoint ({FPM.GetFormattedBytes(size)})";
67 | }
68 |
69 | private void ComponentList_BeforeSelect(object _, TreeViewCancelEventArgs e)
70 | {
71 | if (e.Node.Tag.GetType().ToString().EndsWith("Component"))
72 | {
73 | var component = e.Node.Tag as Component;
74 |
75 | DescriptionBox.Text = "Component Description";
76 | Description.Text = component.Description + $" ({FPM.GetFormattedBytes(component.Size)})";
77 | }
78 | else
79 | {
80 | long categorySize = 0;
81 | FPM.IterateList(e.Node.Nodes, node =>
82 | {
83 | if (node.Tag.GetType().ToString().EndsWith("Component"))
84 | {
85 | categorySize += (node.Tag as Component).Size;
86 | }
87 | });
88 |
89 | DescriptionBox.Text = "Category Description";
90 | Description.Text = (e.Node.Tag as Category).Description + $" ({FPM.GetFormattedBytes(categorySize)})";
91 | }
92 |
93 | if (!DescriptionBox.Visible) DescriptionBox.Visible = true;
94 | }
95 |
96 | private void DestinationPathBrowse_Click(object _, EventArgs e)
97 | {
98 | var pathDialog = new CommonOpenFileDialog() { IsFolderPicker = true };
99 |
100 | if (pathDialog.ShowDialog() == CommonFileDialogResult.Ok)
101 | {
102 | string path = Path.Combine(pathDialog.FileName, "Flashpoint");
103 |
104 | if (FPM.VerifyDestinationPath(path)) DestinationPath.Text = path;
105 | }
106 | }
107 |
108 | private void DestinationPath_KeyPress(object sender, KeyPressEventArgs e)
109 | {
110 | if (e.KeyChar == (char)Keys.Return)
111 | {
112 | InstallButton_Click(this, e);
113 | e.Handled = true;
114 | }
115 | }
116 |
117 | private void InstallButton_Click(object sender, EventArgs e)
118 | {
119 | if (!FPM.VerifyDestinationPath(DestinationPath.Text) || !FPM.CheckDependencies()) return;
120 |
121 | if (Directory.Exists(DestinationPath.Text) && Directory.EnumerateFileSystemEntries(DestinationPath.Text).Any())
122 | {
123 | var pathDialog = MessageBox.Show(
124 | "There are already files in the specified path.\n\n" +
125 | "If you uninstall Flashpoint, these files will be deleted as well.\n\n" +
126 | "Are you sure you want to continue?",
127 | "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning
128 | );
129 |
130 | if (pathDialog == DialogResult.No) return;
131 | }
132 |
133 | if (DestinationPath.Text.Length >= 192)
134 | {
135 | var pathDialog = MessageBox.Show(
136 | "The specified path is extremely long. This may cause certain functionality to break.\n\n" +
137 | "Are you sure you want to continue?",
138 | "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning
139 | );
140 |
141 | if (pathDialog == DialogResult.No) return;
142 | }
143 |
144 | if (!FPM.RedistInstalled)
145 | {
146 | var redistDialog = MessageBox.Show(
147 | "The Flashpoint launcher requires the Visual C++ 2015 x86 redistributable, which you do not appear to have installed.\n\n" +
148 | "It will be installed automatically if you choose to continue.",
149 | "Notice", MessageBoxButtons.OKCancel, MessageBoxIcon.Information
150 | );
151 |
152 | if (redistDialog == DialogResult.Cancel) return;
153 | }
154 |
155 | new Operate().ShowDialog();
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/FlashpointInstaller/src/Forms/Operate.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace FlashpointInstaller
2 | {
3 | partial class Operate
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.CancelButton = new System.Windows.Forms.Button();
32 | this.ProgressMeasure = new System.Windows.Forms.ProgressBar();
33 | this.ProgressLabel = new System.Windows.Forms.Label();
34 | this.SuspendLayout();
35 | //
36 | // CancelButton
37 | //
38 | this.CancelButton.Location = new System.Drawing.Point(410, 41);
39 | this.CancelButton.Name = "CancelButton";
40 | this.CancelButton.Size = new System.Drawing.Size(75, 23);
41 | this.CancelButton.TabIndex = 2;
42 | this.CancelButton.Text = "&Cancel";
43 | this.CancelButton.UseVisualStyleBackColor = true;
44 | this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click);
45 | //
46 | // ProgressMeasure
47 | //
48 | this.ProgressMeasure.Location = new System.Drawing.Point(12, 10);
49 | this.ProgressMeasure.Maximum = 1000;
50 | this.ProgressMeasure.Name = "ProgressMeasure";
51 | this.ProgressMeasure.Size = new System.Drawing.Size(472, 23);
52 | this.ProgressMeasure.TabIndex = 0;
53 | //
54 | // ProgressLabel
55 | //
56 | this.ProgressLabel.AutoSize = true;
57 | this.ProgressLabel.Location = new System.Drawing.Point(9, 46);
58 | this.ProgressLabel.Name = "ProgressLabel";
59 | this.ProgressLabel.Size = new System.Drawing.Size(61, 13);
60 | this.ProgressLabel.TabIndex = 1;
61 | this.ProgressLabel.Text = "Preparing...";
62 | //
63 | // Operate
64 | //
65 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
66 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
67 | this.ClientSize = new System.Drawing.Size(496, 73);
68 | this.ControlBox = false;
69 | this.Controls.Add(this.ProgressLabel);
70 | this.Controls.Add(this.ProgressMeasure);
71 | this.Controls.Add(this.CancelButton);
72 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
73 | this.MaximizeBox = false;
74 | this.MinimizeBox = false;
75 | this.Name = "Operate";
76 | this.ShowInTaskbar = false;
77 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
78 | this.Text = "Installing Flashpoint...";
79 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Operation_FormClosing);
80 | this.Load += new System.EventHandler(this.Operation_Load);
81 | this.ResumeLayout(false);
82 | this.PerformLayout();
83 |
84 | }
85 |
86 | #endregion
87 |
88 | private System.Windows.Forms.Button CancelButton;
89 | private System.Windows.Forms.ProgressBar ProgressMeasure;
90 | private System.Windows.Forms.Label ProgressLabel;
91 | }
92 | }
--------------------------------------------------------------------------------
/FlashpointInstaller/src/Forms/Operate.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Net;
7 | using System.Threading.Tasks;
8 | using System.Windows.Forms;
9 |
10 | using FlashpointInstaller.Common;
11 | using Microsoft.WindowsAPICodePack.Taskbar;
12 | using SharpCompress.Archives.Zip;
13 | using SharpCompress.Common;
14 | using SharpCompress.Readers;
15 |
16 | namespace FlashpointInstaller
17 | {
18 | public partial class Operate : Form
19 | {
20 | Component workingComponent;
21 |
22 | List markedComponents = new List();
23 |
24 | Stream stream;
25 | ZipArchive archive;
26 | IReader reader;
27 |
28 | List extractedFiles = new List();
29 |
30 | long byteProgress = 0;
31 | long byteTotal = 0;
32 |
33 | int cancelStatus = 0;
34 |
35 | public Operate() => InitializeComponent();
36 |
37 | private async void Operation_Load(object sender, EventArgs e)
38 | {
39 | TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Normal, FPM.Main.Handle);
40 |
41 | FPM.Client.DownloadProgressChanged += OnDownloadProgressChanged;
42 |
43 | FPM.IterateList(FPM.Main.ComponentList.Nodes, node =>
44 | {
45 | if (node.Checked && node.Tag.GetType().ToString().EndsWith("Component"))
46 | {
47 | var component = node.Tag as Component;
48 |
49 | markedComponents.Add(component);
50 | byteTotal += component.Size;
51 | }
52 | });
53 |
54 | foreach (var component in markedComponents)
55 | {
56 | workingComponent = component;
57 |
58 | if (component.Size > 0)
59 | {
60 | while (true)
61 | {
62 | try
63 | {
64 | stream = new MemoryStream(await FPM.Client.DownloadDataTaskAsync(new Uri(component.URL)));
65 | }
66 | catch (WebException ex)
67 | {
68 | if (ex.Status != WebExceptionStatus.RequestCanceled)
69 | {
70 | var errorResult = MessageBox.Show(
71 | $"The {workingComponent.Title} component failed to download.\n\n" +
72 | "Click OK to retry, or Cancel to abort the installation.",
73 | "Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Error
74 | );
75 |
76 | if (errorResult == DialogResult.OK) continue;
77 | }
78 |
79 | cancelStatus = 2;
80 | CancelButton.PerformClick();
81 | return;
82 | }
83 |
84 | break;
85 | }
86 | }
87 |
88 | await Task.Run(ExtractComponents);
89 |
90 | byteProgress += component.Size;
91 | }
92 |
93 | if (cancelStatus == 0)
94 | {
95 | if (!FPM.RedistInstalled)
96 | {
97 | ProgressLabel.Text = "Installing Visual C++ 2015 x86 redistributable...";
98 | CancelButton.Enabled = false;
99 |
100 | string redistPath = Path.GetTempPath() + "vc_redist.x86.exe";
101 |
102 | if (!File.Exists(redistPath))
103 | {
104 | FPM.Client.DownloadProgressChanged -= OnDownloadProgressChanged;
105 | await FPM.Client.DownloadFileTaskAsync("https://aka.ms/vs/17/release/vc_redist.x86.exe", redistPath);
106 | }
107 |
108 | await Task.Run(() =>
109 | {
110 | var redistProcess = Process.Start(redistPath, "/install /norestart /quiet");
111 | redistProcess.WaitForExit();
112 |
113 | if (redistProcess.ExitCode != 0)
114 | {
115 | MessageBox.Show(
116 | "Failed to install Visual C++ 2015 x86 redistributable.\n\n" +
117 | "You can try installing it manually from https://aka.ms/vs/17/release/vc_redist.x86.exe.",
118 | "Error", MessageBoxButtons.OK, MessageBoxIcon.Error
119 | );
120 | }
121 | });
122 | }
123 |
124 | FinishOperation();
125 | }
126 | }
127 |
128 | private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
129 | {
130 | if (cancelStatus != 0)
131 | {
132 | FPM.Client.CancelAsync();
133 | return;
134 | }
135 |
136 | double currentProgress = (double)e.BytesReceived / e.TotalBytesToReceive;
137 | long currentSize = workingComponent.Size;
138 | double totalProgress = (byteProgress + (currentProgress / 2 * currentSize)) / byteTotal;
139 |
140 | ProgressMeasure.Invoke((MethodInvoker)delegate
141 | {
142 | ProgressMeasure.Value = (int)((double)totalProgress * ProgressMeasure.Maximum);
143 | });
144 |
145 | ProgressLabel.Invoke((MethodInvoker)delegate
146 | {
147 | ProgressLabel.Text =
148 | $"[{(int)((double)totalProgress * 100)}%] Downloading component \"{workingComponent.Title}\"... " +
149 | $"{FPM.GetFormattedBytes(e.BytesReceived)} of {FPM.GetFormattedBytes(e.TotalBytesToReceive)}";
150 | });
151 |
152 | FPM.Main.Invoke((MethodInvoker)delegate
153 | {
154 | TaskbarManager.Instance.SetProgressValue(
155 | (int)((double)totalProgress * ProgressMeasure.Maximum), ProgressMeasure.Maximum, FPM.Main.Handle
156 | );
157 | });
158 | }
159 |
160 | private void ExtractComponents()
161 | {
162 | string rootPath = FPM.Main.DestinationPath.Text;
163 | string infoPath = Path.Combine(FPM.Main.DestinationPath.Text, "Components");
164 | string infoFile = Path.Combine(infoPath, workingComponent.ID);
165 |
166 | Directory.CreateDirectory(infoPath);
167 |
168 | using (TextWriter writer = File.CreateText(infoFile))
169 | {
170 | string[] header = new List
171 | { workingComponent.Hash, $"{workingComponent.Size}" }.Concat(workingComponent.Depends).ToArray();
172 |
173 | writer.WriteLine(string.Join(" ", header));
174 | }
175 |
176 | extractedFiles.Add(infoFile);
177 |
178 | if (workingComponent.Size == 0) return;
179 |
180 | using (archive = ZipArchive.Open(stream))
181 | {
182 | using (reader = archive.ExtractAllEntries())
183 | {
184 | long extractedSize = 0;
185 | long totalSize = archive.TotalUncompressSize;
186 |
187 | string destPath = Path.Combine(rootPath, workingComponent.Path.Replace('/', '\\'));
188 |
189 | while (cancelStatus == 0 && reader.MoveToNextEntry())
190 | {
191 | if (reader.Entry.IsDirectory) continue;
192 |
193 | Directory.CreateDirectory(destPath);
194 |
195 | reader.WriteEntryToDirectory(destPath, new ExtractionOptions {
196 | ExtractFullPath = true, Overwrite = true, PreserveFileTime = true
197 | });
198 |
199 | extractedFiles.Add(Path.Combine(destPath, reader.Entry.Key.Replace('/', '\\')));
200 |
201 | using (TextWriter writer = File.AppendText(infoFile))
202 | {
203 | writer.WriteLine(Path.Combine(workingComponent.Path, reader.Entry.Key).Replace("/", @"\"));
204 | }
205 |
206 | extractedSize += reader.Entry.Size;
207 |
208 | double currentProgress = (double)extractedSize / totalSize;
209 | long currentSize = workingComponent.Size;
210 | double totalProgress = (byteProgress + (currentSize / 2) + (currentProgress / 2 * currentSize)) / byteTotal;
211 |
212 | ProgressMeasure.Invoke((MethodInvoker)delegate
213 | {
214 | ProgressMeasure.Value = (int)((double)totalProgress * ProgressMeasure.Maximum);
215 | });
216 |
217 | ProgressLabel.Invoke((MethodInvoker)delegate
218 | {
219 | ProgressLabel.Text =
220 | $"[{(int)((double)totalProgress * 100)}%] Extracting component \"{workingComponent.Title}\"... " +
221 | $"{FPM.GetFormattedBytes(extractedSize)} of {FPM.GetFormattedBytes(totalSize)}";
222 | });
223 |
224 | FPM.Main.Invoke((MethodInvoker)delegate
225 | {
226 | TaskbarManager.Instance.SetProgressValue(
227 | (int)((double)totalProgress * ProgressMeasure.Maximum), ProgressMeasure.Maximum, FPM.Main.Handle
228 | );
229 | });
230 | }
231 |
232 | if (cancelStatus != 0)
233 | {
234 | reader.Cancel();
235 | cancelStatus = 2;
236 | }
237 | }
238 | }
239 | }
240 |
241 | private async void FinishOperation()
242 | {
243 | TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress, FPM.Main.Handle);
244 |
245 | await Task.Run(() =>
246 | {
247 | var shortcutPaths = new List { FPM.Main.DestinationPath.Text };
248 |
249 | if (FPM.Main.ShortcutDesktop.Checked)
250 | {
251 | shortcutPaths.Add(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
252 | }
253 | if (FPM.Main.ShortcutStartMenu.Checked)
254 | {
255 | if (Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Programs)))
256 | {
257 | shortcutPaths.Add(Environment.GetFolderPath(Environment.SpecialFolder.Programs));
258 | }
259 | else
260 | {
261 | shortcutPaths.Add(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu));
262 | }
263 | }
264 |
265 | foreach (string path in shortcutPaths)
266 | {
267 | var shortcut = new IWshRuntimeLibrary.WshShell().CreateShortcut(Path.Combine(path, "Flashpoint.lnk"));
268 | shortcut.TargetPath = Path.Combine(FPM.Main.DestinationPath.Text, "Launcher", "Flashpoint.exe");
269 | shortcut.WorkingDirectory = Path.Combine(FPM.Main.DestinationPath.Text, "Launcher");
270 | shortcut.Description = "Shortcut to Flashpoint";
271 | shortcut.Save();
272 | }
273 | });
274 |
275 | Hide();
276 | FPM.Main.Hide();
277 |
278 | new Finish().ShowDialog();
279 | }
280 |
281 | private async void CancelButton_Click(object sender, EventArgs e)
282 | {
283 | if (cancelStatus < 1) cancelStatus = 1;
284 |
285 | CancelButton.Enabled = false;
286 | ProgressLabel.Invoke((MethodInvoker)delegate
287 | {
288 | ProgressLabel.Text = "Cancelling...";
289 | });
290 |
291 | await Task.Run(() =>
292 | {
293 | while (cancelStatus != 2) { }
294 |
295 | if (Directory.Exists(FPM.Main.DestinationPath.Text))
296 | {
297 | foreach (string file in extractedFiles)
298 | {
299 | try { File.Delete(file); } catch { }
300 |
301 | string folder = Path.GetDirectoryName(file);
302 |
303 | while (folder != Directory.GetParent(FPM.Main.DestinationPath.Text).ToString())
304 | {
305 | if (Directory.Exists(folder) && !Directory.EnumerateFiles(folder, "*", SearchOption.AllDirectories).Any())
306 | {
307 | try { Directory.Delete(folder, true); } catch { }
308 | }
309 | else break;
310 |
311 | folder = Directory.GetParent(folder).ToString();
312 | }
313 | }
314 | }
315 | });
316 |
317 | TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress, FPM.Main.Handle);
318 |
319 | Close();
320 | }
321 |
322 | private void Operation_FormClosing(object sender, FormClosingEventArgs e)
323 | {
324 | if (cancelStatus != 2) e.Cancel = true;
325 |
326 | FPM.Client.DownloadProgressChanged -= OnDownloadProgressChanged;
327 | }
328 | }
329 | }
330 |
--------------------------------------------------------------------------------
/FlashpointInstaller/src/Forms/Operate.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/FlashpointInstaller/src/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Net;
4 | using System.Windows.Forms;
5 | using System.Xml;
6 |
7 | using FlashpointInstaller.Common;
8 |
9 | namespace FlashpointInstaller
10 | {
11 | internal static class Program
12 | {
13 | ///
14 | /// The main entry point for the application.
15 | ///
16 | [STAThread]
17 | static void Main(string[] args)
18 | {
19 | Application.EnableVisualStyles();
20 | Application.SetCompatibleTextRenderingDefault(false);
21 |
22 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
23 |
24 | try
25 | {
26 | var listStream = new MemoryStream(FPM.Client.DownloadData(FPM.ListURL)) { Position = 0 };
27 |
28 | FPM.XmlTree = new XmlDocument();
29 | FPM.XmlTree.Load(listStream);
30 | }
31 | catch
32 | {
33 | MessageBox.Show(
34 | "The component list could not be downloaded!\n\n" +
35 | "Verify that your internet connection is working.",
36 | "Error", MessageBoxButtons.OK, MessageBoxIcon.Error
37 | );
38 |
39 | Environment.Exit(1);
40 | }
41 |
42 | Application.Run(new Main());
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/FlashpointInstaller/src/TriStateTreeView.cs:
--------------------------------------------------------------------------------
1 | // Copyright (CPOL) 2011 RikTheVeggie - see http://www.codeproject.com/info/cpol10.aspx
2 | // Tri-State Tree View http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=202435
3 |
4 | namespace RikTheVeggie
5 | {
6 | //
7 | // A Tri-State TreeView designed for on-demand populating of the tree
8 | //
9 | //
10 | // 'Mixed' nodes retain their checked state, meaning they can be checked or unchecked according to their current state
11 | // Tree can be navigated by keyboard (cursor keys & space)
12 | // No need to do anything special in calling code
13 | //
14 | public class TriStateTreeView : System.Windows.Forms.TreeView
15 | {
16 | //
17 | // CheckedState is an enum of all allowable nodes states
18 | //
19 | public enum CheckedState : int { UnInitialised = -1, UnChecked, Checked, Mixed };
20 |
21 | //
22 | // IgnoreClickAction is used to ingore messages generated by setting the node.Checked flag in code
23 | // Do not set e.Cancel = true in OnBeforeCheck otherwise the Checked state will be lost
24 | //
25 | int IgnoreClickAction = 0;
26 | //
27 |
28 | // TriStateStyles is an enum of all allowable tree styles
29 | // All styles check children when parent is checked
30 | // Installer automatically checks parent if all children are checked, and unchecks parent if at least one child is unchecked
31 | // Standard never changes the checked status of a parent
32 | //
33 | public enum TriStateStyles : int { Standard = 0, Installer };
34 |
35 | // Create a private member for the tree style, and allow it to be set on the property sheer
36 | private TriStateStyles TriStateStyle = TriStateStyles.Standard;
37 |
38 | [System.ComponentModel.Category("Tri-State Tree View")]
39 | [System.ComponentModel.DisplayName("Style")]
40 | [System.ComponentModel.Description("Style of the Tri-State Tree View")]
41 | public TriStateStyles TriStateStyleProperty
42 | {
43 | get { return TriStateStyle; }
44 | set { TriStateStyle = value; }
45 | }
46 |
47 | //
48 | // Constructor. Create and populate an image list
49 | //
50 | public TriStateTreeView() : base()
51 | {
52 | StateImageList = new System.Windows.Forms.ImageList();
53 |
54 | // populate the image list, using images from the System.Windows.Forms.CheckBoxRenderer class
55 | for (int i = 0; i < 3; i++)
56 | {
57 | // Create a bitmap which holds the relevent check box style
58 | // see http://msdn.microsoft.com/en-us/library/ms404307.aspx and http://msdn.microsoft.com/en-us/library/system.windows.forms.checkboxrenderer.aspx
59 |
60 | System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(16, 16);
61 | System.Drawing.Graphics chkGraphics = System.Drawing.Graphics.FromImage(bmp);
62 | switch ( i )
63 | {
64 | // 0,1 - offset the checkbox slightly so it positions in the correct place
65 | case 0:
66 | System.Windows.Forms.CheckBoxRenderer.DrawCheckBox(chkGraphics, new System.Drawing.Point(0, 1), System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);
67 | break;
68 | case 1:
69 | System.Windows.Forms.CheckBoxRenderer.DrawCheckBox(chkGraphics, new System.Drawing.Point(0, 1), System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal);
70 | break;
71 | case 2:
72 | System.Windows.Forms.CheckBoxRenderer.DrawCheckBox(chkGraphics, new System.Drawing.Point(0, 1), System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal);
73 | break;
74 | }
75 |
76 | StateImageList.Images.Add(bmp);
77 | }
78 | }
79 |
80 | //
81 | // Called once before window displayed. Disables default Checkbox functionality and ensures all nodes display an 'unchecked' image.
82 | //
83 | protected override void OnCreateControl()
84 | {
85 | base.OnCreateControl();
86 | CheckBoxes = false; // Disable default CheckBox functionality if it's been enabled
87 |
88 | // Give every node an initial 'unchecked' image
89 | IgnoreClickAction++; // we're making changes to the tree, ignore any other change requests
90 | UpdateChildState(this.Nodes, (int)CheckedState.UnChecked, false, true);
91 | IgnoreClickAction--;
92 | }
93 |
94 | //
95 | // Called after a node is checked. Forces all children to inherit current state, and notifies parents they may need to become 'mixed'
96 | //
97 | protected override void OnAfterCheck(System.Windows.Forms.TreeViewEventArgs e)
98 | {
99 | base.OnAfterCheck(e);
100 |
101 | if (IgnoreClickAction > 0)
102 | {
103 | return;
104 | }
105 |
106 | IgnoreClickAction++; // we're making changes to the tree, ignore any other change requests
107 |
108 | // the checked state has already been changed, we just need to update the state index
109 |
110 | // node is either ticked or unticked. ignore mixed state, as the node is still only ticked or unticked regardless of state of children
111 | System.Windows.Forms.TreeNode tn = e.Node;
112 | tn.StateImageIndex = tn.Checked ? (int)CheckedState.Checked : (int)CheckedState.UnChecked;
113 |
114 | // force all children to inherit the same state as the current node
115 | UpdateChildState(e.Node.Nodes, e.Node.StateImageIndex, e.Node.Checked, false);
116 |
117 | // populate state up the tree, possibly resulting in parents with mixed state
118 | UpdateParentState(e.Node.Parent);
119 |
120 | IgnoreClickAction--;
121 | }
122 |
123 | //
124 | // Called after a node is expanded. Ensures any new nodes display an 'unchecked' image
125 | //
126 | protected override void OnAfterExpand(System.Windows.Forms.TreeViewEventArgs e)
127 | {
128 | // If any child node is new, give it the same check state as the current node
129 | // So if current node is ticked, child nodes will also be ticked
130 | base.OnAfterExpand(e);
131 |
132 | IgnoreClickAction++; // we're making changes to the tree, ignore any other change requests
133 | UpdateChildState(e.Node.Nodes, e.Node.StateImageIndex, e.Node.Checked, true);
134 | IgnoreClickAction--;
135 | }
136 |
137 | //
138 | // Helper function to replace child state with that of the parent
139 | //
140 | protected void UpdateChildState(System.Windows.Forms.TreeNodeCollection Nodes, int StateImageIndex, bool Checked, bool ChangeUninitialisedNodesOnly)
141 | {
142 | foreach (System.Windows.Forms.TreeNode tnChild in Nodes)
143 | {
144 | if (!ChangeUninitialisedNodesOnly || tnChild.StateImageIndex == -1)
145 | {
146 | tnChild.StateImageIndex = StateImageIndex;
147 | tnChild.Checked = Checked; // override 'checked' state of child with that of parent
148 |
149 | if (tnChild.Nodes.Count > 0)
150 | {
151 | UpdateChildState(tnChild.Nodes, StateImageIndex, Checked, ChangeUninitialisedNodesOnly);
152 | }
153 | }
154 | }
155 | }
156 |
157 | //
158 | // Helper function to notify parent it may need to use 'mixed' state
159 | //
160 | protected void UpdateParentState(System.Windows.Forms.TreeNode tn)
161 | {
162 | // Node needs to check all of it's children to see if any of them are ticked or mixed
163 | if (tn == null)
164 | return;
165 |
166 | int OrigStateImageIndex = tn.StateImageIndex;
167 |
168 | int UnCheckedNodes = 0, CheckedNodes = 0, MixedNodes = 0;
169 |
170 | // The parent needs to know how many of it's children are Checked or Mixed
171 | foreach (System.Windows.Forms.TreeNode tnChild in tn.Nodes)
172 | {
173 | if (tnChild.StateImageIndex == (int)CheckedState.Checked)
174 | CheckedNodes++;
175 | else if (tnChild.StateImageIndex == (int)CheckedState.Mixed)
176 | {
177 | MixedNodes++;
178 | break;
179 | }
180 | else
181 | UnCheckedNodes++;
182 | }
183 |
184 | if (TriStateStyle == TriStateStyles.Installer)
185 | {
186 | // In Installer mode, if all child nodes are checked then parent is checked
187 | // If at least one child is unchecked, then parent is unchecked
188 | if (MixedNodes == 0)
189 | {
190 | if (UnCheckedNodes == 0)
191 | {
192 | // all children are checked, so parent must be checked
193 | tn.Checked = true;
194 | }
195 | else
196 | {
197 | // at least one child is unchecked, so parent must be unchecked
198 | tn.Checked = false;
199 | }
200 | }
201 | }
202 |
203 | // Determine the parent's new Image State
204 | if (MixedNodes > 0)
205 | {
206 | // at least one child is mixed, so parent must be mixed
207 | tn.StateImageIndex = (int)CheckedState.Mixed;
208 | }
209 | else if (CheckedNodes > 0 && UnCheckedNodes == 0)
210 | {
211 | // all children are checked
212 | if (tn.Checked)
213 | tn.StateImageIndex = (int)CheckedState.Checked;
214 | else
215 | tn.StateImageIndex = (int)CheckedState.Mixed;
216 | }
217 | else if (CheckedNodes > 0)
218 | {
219 | // some children are checked, the rest are unchecked
220 | tn.StateImageIndex = (int)CheckedState.Mixed;
221 | }
222 | else
223 | {
224 | // all children are unchecked
225 | if (tn.Checked)
226 | tn.StateImageIndex = (int)CheckedState.Mixed;
227 | else
228 | tn.StateImageIndex = (int)CheckedState.UnChecked;
229 | }
230 |
231 | if (OrigStateImageIndex != tn.StateImageIndex && tn.Parent != null)
232 | {
233 | // Parent's state has changed, notify the parent's parent
234 | UpdateParentState(tn.Parent);
235 | }
236 | }
237 |
238 | //
239 | // Called on keypress. Used to change node state when Space key is pressed
240 | // Invokes OnAfterCheck to do the real work
241 | //
242 | protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e)
243 | {
244 | base.OnKeyDown(e);
245 |
246 | // is the keypress a space? If not, discard it
247 | if (e.KeyCode == System.Windows.Forms.Keys.Space)
248 | {
249 | // toggle the node's checked status. This will then fire OnAfterCheck
250 | SelectedNode.Checked = !SelectedNode.Checked;
251 | }
252 | }
253 |
254 | //
255 | // Called when node is clicked by the mouse. Does nothing unless the image was clicked
256 | // Invokes OnAfterCheck to do the real work
257 | //
258 | protected override void OnNodeMouseClick(System.Windows.Forms.TreeNodeMouseClickEventArgs e)
259 | {
260 | base.OnNodeMouseClick(e);
261 |
262 | // is the click on the checkbox? If not, discard it
263 | System.Windows.Forms.TreeViewHitTestInfo info = HitTest(e.X, e.Y);
264 | if (info == null || info.Location != System.Windows.Forms.TreeViewHitTestLocations.StateImage)
265 | {
266 | return;
267 | }
268 |
269 | // toggle the node's checked status. This will then fire OnAfterCheck
270 | System.Windows.Forms.TreeNode tn = e.Node;
271 | tn.Checked = !tn.Checked;
272 | }
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/FlashpointManager/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/FlashpointManager/FlashpointManager.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FlashpointProject/FlashpointComponentTools/0628df6e079542f566c170ca16501e420ab04b2c/FlashpointManager/FlashpointManager.ico
--------------------------------------------------------------------------------
/FlashpointManager/FlashpointManager.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32616.157
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlashpointManager", "FlashpointManager.csproj", "{3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Debug|x86 = Debug|x86
12 | Release|Any CPU = Release|Any CPU
13 | Release|x86 = Release|x86
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Debug|x86.ActiveCfg = Debug|x86
19 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Debug|x86.Build.0 = Debug|x86
20 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Release|x86.ActiveCfg = Release|x86
23 | {3A52CEC9-3EEC-4ED4-8E21-51635A08AC38}.Release|x86.Build.0 = Release|x86
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {F7D16A14-B30F-464C-9278-7F0A094AD68F}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/FlashpointManager/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("Flashpoint Manager")]
8 | [assembly: AssemblyDescription("Add, update, or remove Flashpoint components.")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("Flashpoint Manager")]
12 | [assembly: AssemblyCopyright("")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("3a52cec9-3eec-4ed4-8e21-51635a08ac38")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.3.0.0")]
35 | [assembly: AssemblyFileVersion("1.3")]
36 |
--------------------------------------------------------------------------------
/FlashpointManager/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace FlashpointManager.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FlashpointManager.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/FlashpointManager/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/FlashpointManager/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace FlashpointManager.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.2.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/FlashpointManager/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/FlashpointManager/Properties/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
52 | true
53 | PerMonitorV2
54 |
55 |
56 |
57 |
71 |
--------------------------------------------------------------------------------
/FlashpointManager/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/FlashpointManager/src/Forms/CheckFiles.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace FlashpointManager.src.Forms
2 | {
3 | partial class CheckFiles
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CheckFiles));
32 | this.FileList = new System.Windows.Forms.ListView();
33 | this.FileComponent = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
34 | this.FileLocation = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
35 | this.RedownloadButton = new System.Windows.Forms.Button();
36 | this.CancelButton = new System.Windows.Forms.Button();
37 | this.FileMessage = new System.Windows.Forms.LinkLabel();
38 | this.FileListLoading = new System.Windows.Forms.Label();
39 | this.SuspendLayout();
40 | //
41 | // FileList
42 | //
43 | this.FileList.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
44 | this.FileComponent,
45 | this.FileLocation});
46 | this.FileList.Cursor = System.Windows.Forms.Cursors.Default;
47 | this.FileList.FullRowSelect = true;
48 | this.FileList.GridLines = true;
49 | this.FileList.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.Nonclickable;
50 | this.FileList.HideSelection = false;
51 | this.FileList.Location = new System.Drawing.Point(12, 53);
52 | this.FileList.MultiSelect = false;
53 | this.FileList.Name = "FileList";
54 | this.FileList.Size = new System.Drawing.Size(460, 160);
55 | this.FileList.Sorting = System.Windows.Forms.SortOrder.Ascending;
56 | this.FileList.TabIndex = 1;
57 | this.FileList.UseCompatibleStateImageBehavior = false;
58 | this.FileList.View = System.Windows.Forms.View.Details;
59 | this.FileList.Visible = false;
60 | //
61 | // FileComponent
62 | //
63 | this.FileComponent.Text = "Component";
64 | this.FileComponent.Width = 120;
65 | //
66 | // FileLocation
67 | //
68 | this.FileLocation.Text = "File Location";
69 | this.FileLocation.Width = 315;
70 | //
71 | // RedownloadButton
72 | //
73 | this.RedownloadButton.Enabled = false;
74 | this.RedownloadButton.Location = new System.Drawing.Point(11, 224);
75 | this.RedownloadButton.Name = "RedownloadButton";
76 | this.RedownloadButton.Size = new System.Drawing.Size(356, 26);
77 | this.RedownloadButton.TabIndex = 2;
78 | this.RedownloadButton.Text = "&Redownload components";
79 | this.RedownloadButton.UseVisualStyleBackColor = true;
80 | this.RedownloadButton.Click += new System.EventHandler(this.RedownloadButton_Click);
81 | //
82 | // CancelButton
83 | //
84 | this.CancelButton.Location = new System.Drawing.Point(373, 224);
85 | this.CancelButton.Name = "CancelButton";
86 | this.CancelButton.Size = new System.Drawing.Size(100, 26);
87 | this.CancelButton.TabIndex = 3;
88 | this.CancelButton.Text = "&Cancel";
89 | this.CancelButton.UseVisualStyleBackColor = true;
90 | this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click);
91 | //
92 | // FileMessage
93 | //
94 | this.FileMessage.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
95 | this.FileMessage.LinkArea = new System.Windows.Forms.LinkArea(143, 14);
96 | this.FileMessage.Location = new System.Drawing.Point(12, 9);
97 | this.FileMessage.Name = "FileMessage";
98 | this.FileMessage.Size = new System.Drawing.Size(470, 40);
99 | this.FileMessage.TabIndex = 0;
100 | this.FileMessage.TabStop = true;
101 | this.FileMessage.Text = resources.GetString("FileMessage.Text");
102 | this.FileMessage.UseCompatibleTextRendering = true;
103 | this.FileMessage.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.FileMessage_LinkClicked);
104 | //
105 | // FileListLoading
106 | //
107 | this.FileListLoading.BackColor = System.Drawing.SystemColors.Control;
108 | this.FileListLoading.Location = new System.Drawing.Point(12, 117);
109 | this.FileListLoading.Name = "FileListLoading";
110 | this.FileListLoading.Size = new System.Drawing.Size(460, 23);
111 | this.FileListLoading.TabIndex = 6;
112 | this.FileListLoading.Text = "Searching for missing files...";
113 | this.FileListLoading.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
114 | //
115 | // CheckFiles
116 | //
117 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
118 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
119 | this.ClientSize = new System.Drawing.Size(484, 261);
120 | this.Controls.Add(this.FileListLoading);
121 | this.Controls.Add(this.FileMessage);
122 | this.Controls.Add(this.CancelButton);
123 | this.Controls.Add(this.RedownloadButton);
124 | this.Controls.Add(this.FileList);
125 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
126 | this.MaximizeBox = false;
127 | this.MinimizeBox = false;
128 | this.Name = "CheckFiles";
129 | this.ShowIcon = false;
130 | this.ShowInTaskbar = false;
131 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
132 | this.Text = "Missing File Checker";
133 | this.Load += new System.EventHandler(this.CheckFiles_Load);
134 | this.ResumeLayout(false);
135 |
136 | }
137 |
138 | #endregion
139 |
140 | private System.Windows.Forms.ListView FileList;
141 | private System.Windows.Forms.ColumnHeader FileComponent;
142 | private System.Windows.Forms.ColumnHeader FileLocation;
143 | private System.Windows.Forms.Button RedownloadButton;
144 | private System.Windows.Forms.Button CancelButton;
145 | private System.Windows.Forms.LinkLabel FileMessage;
146 | private System.Windows.Forms.Label FileListLoading;
147 | }
148 | }
--------------------------------------------------------------------------------
/FlashpointManager/src/Forms/CheckFiles.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using System.Windows.Forms;
7 |
8 | using FlashpointManager.Common;
9 |
10 | namespace FlashpointManager.src.Forms
11 | {
12 | public partial class CheckFiles : Form
13 | {
14 | public CheckFiles() => InitializeComponent();
15 |
16 | private async void CheckFiles_Load(object sender, EventArgs e)
17 | {
18 | FPM.ComponentTracker.Broken.Clear();
19 |
20 | await Task.Run(() =>
21 | {
22 | FPM.IterateXML(FPM.XmlTree.GetElementsByTagName("list")[0].ChildNodes, node =>
23 | {
24 | if (node.Name != "component") return;
25 |
26 | var component = new Component(node);
27 |
28 | if (!component.Downloaded) return;
29 |
30 | bool added = false;
31 |
32 | foreach (string file in File.ReadLines(component.InfoFile).Skip(1))
33 | {
34 | if (!File.Exists(Path.Combine(FPM.SourcePath, file)))
35 | {
36 | if (!added)
37 | {
38 | FPM.ComponentTracker.Broken.Add(component);
39 | added = true;
40 | }
41 |
42 | var item = new ListViewItem();
43 | item.Text = component.Title;
44 | item.SubItems.Add(file);
45 |
46 | FileList.Invoke((MethodInvoker)delegate { FileList.Items.Add(item); });
47 | }
48 | }
49 | });
50 | });
51 |
52 | FileListLoading.Visible = false;
53 | FileList.Visible = true;
54 |
55 | if (FPM.ComponentTracker.Outdated.Count > 0)
56 | {
57 | RedownloadButton.Text = "Cannot redownload components with pending updates";
58 | }
59 | else if (FPM.OfflineMode)
60 | {
61 | RedownloadButton.Text = "Cannot redownload components in offline mode";
62 | }
63 | else
64 | {
65 | RedownloadButton.Enabled = FileList.Items.Count > 0;
66 | }
67 | }
68 |
69 | private void FileMessage_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
70 | {
71 | Process.Start(
72 | new ProcessStartInfo("https://bluemaxima.org/flashpoint/datahub/Troubleshooting_Antivirus_Interference")
73 | {
74 | UseShellExecute = true
75 | }
76 | );
77 | }
78 |
79 | private void RedownloadButton_Click(object sender, EventArgs e)
80 | {
81 | FPM.OperationMode = OperateMode.Repair;
82 |
83 | new Operate().ShowDialog();
84 |
85 | Close();
86 | }
87 |
88 | private void CancelButton_Click(object sender, EventArgs e) => Close();
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/FlashpointManager/src/Forms/CheckFiles.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | If missing files are reappearing in the list even after redownloading the affected components, it may be the result of antivirus interference. Read this page to learn how to create an antivirus exception for Flashpoint.
122 |
123 |
--------------------------------------------------------------------------------
/FlashpointManager/src/Forms/Main.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Diagnostics;
4 | using System.Drawing;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Windows.Forms;
8 | using System.Xml;
9 |
10 | using FlashpointManager.Common;
11 |
12 | namespace FlashpointManager
13 | {
14 | public partial class Main : Form
15 | {
16 | public Main() => InitializeComponent();
17 |
18 | private void Main_Load(object sender, EventArgs e)
19 | {
20 | this.TabControl.ItemSize = new Size(this.TabControl.Width / this.TabControl.TabCount, 0) - new Size(1, 0);
21 | Text += $" v{Application.ProductVersion}";
22 |
23 | if (FPM.OfflineMode)
24 | {
25 | TabControl.TabPages.RemoveAt(1);
26 | OfflineIndicator.Visible = true;
27 | }
28 |
29 | using (XmlNodeList rootElements = FPM.XmlTree.GetElementsByTagName("list"))
30 | {
31 | if (rootElements.Count > 0)
32 | {
33 | FPM.RecursiveAddToList(rootElements[0], ComponentList.Nodes);
34 | }
35 | else
36 | {
37 | FPM.ParseError("Root element was not found.");
38 | }
39 | }
40 |
41 | FPM.SyncManager();
42 | ComponentList.BeforeCheck += ComponentList_BeforeCheck;
43 | UpdateList.ItemChecked += UpdateList_ItemChecked;
44 | TabControl.SelectedIndexChanged += tabControl_SelectedIndexChanged;
45 |
46 | if (FPM.AutoDownload.Count > 0)
47 | {
48 | foreach (string id in FPM.AutoDownload)
49 | {
50 | var query = ComponentList.Nodes.Find(id, true);
51 | if (query.Length > 0) query[0].Checked = true;
52 | }
53 |
54 | FPM.CheckDependencies(false);
55 |
56 | new Operate()
57 | {
58 | StartPosition = FormStartPosition.CenterScreen,
59 | TopMost = true
60 | }.ShowDialog();
61 |
62 | Close();
63 | }
64 |
65 | if (FPM.OpenUpdateTab)
66 | {
67 | TabControl.SelectTab(1);
68 | }
69 | else
70 | {
71 | tabControl_SelectedIndexChanged(this, EventArgs.Empty);
72 | }
73 | }
74 |
75 | public void ComponentList_BeforeCheck(object sender, TreeViewCancelEventArgs e)
76 | {
77 | if (e.Node.ForeColor == Color.FromArgb(255, 96, 96, 96) && e.Node.Checked)
78 | {
79 | e.Cancel = true;
80 | }
81 | }
82 |
83 | public void ComponentList_AfterCheck(object sender, TreeViewEventArgs e)
84 | {
85 | if (!e.Node.Tag.GetType().ToString().EndsWith("Component")) return;
86 |
87 | var component = e.Node.Tag as Common.Component;
88 |
89 | if (component.Checked == e.Node.Checked) return;
90 | component.Checked = e.Node.Checked;
91 |
92 | if (!FPM.Ready) return;
93 |
94 | FPM.ModifiedSize += (e.Node.Checked ? 1 : -1) * (FPM.ComponentTracker.Downloaded.Exists(c => c.ID == component.ID)
95 | ? long.Parse(File.ReadLines(component.InfoFile).First().Split(' ')[1])
96 | : component.Size);
97 |
98 | ChangeButton.Text = $"Apply changes ({FPM.GetFormattedBytes(FPM.ModifiedSize, true)})";
99 | ChangeButton.Enabled = true;
100 | }
101 |
102 | private void ComponentList_BeforeSelect(object sender, TreeViewCancelEventArgs e)
103 | {
104 | if (e.Node.Tag.GetType().ToString().EndsWith("Component"))
105 | {
106 | var component = e.Node.Tag as Common.Component;
107 |
108 | DescriptionBox.Text = "Component Description";
109 | Description.Text = component.Description + $" ({FPM.GetFormattedBytes(component.Size)})";
110 | }
111 | else
112 | {
113 | long categorySize = 0;
114 | FPM.IterateList(e.Node.Nodes, node =>
115 | {
116 | if (node.Tag.GetType().ToString().EndsWith("Component"))
117 | {
118 | categorySize += (node.Tag as Common.Component).Size;
119 | }
120 | });
121 |
122 | DescriptionBox.Text = "Category Description";
123 | Description.Text = (e.Node.Tag as Category).Description + $" ({FPM.GetFormattedBytes(categorySize)})";
124 | }
125 |
126 | if (!DescriptionBox.Visible) DescriptionBox.Visible = true;
127 | }
128 |
129 | private void ChangeButton_Click(object sender, EventArgs e)
130 | {
131 | if (!FPM.CheckDependencies()) return;
132 |
133 | FPM.OperationMode = OperateMode.Modify;
134 |
135 | new Operate().ShowDialog();
136 | }
137 |
138 | private void UpdateButton_Click(object sender, EventArgs e)
139 | {
140 | FPM.OperationMode = OperateMode.Update;
141 |
142 | if (UpdateList.CheckedItems.Count == 0)
143 | {
144 | MessageBox.Show("No items are checked.", "Flashpoint Manager", MessageBoxButtons.OK, MessageBoxIcon.Information);
145 | return;
146 | }
147 |
148 | new Operate().ShowDialog();
149 | }
150 |
151 | private void Main_FormClosing(object sender, FormClosingEventArgs e)
152 | {
153 | if (FPM.OpenLauncherOnClose)
154 | {
155 | new Process() { StartInfo = {
156 | UseShellExecute = true,
157 | FileName = "Flashpoint.exe",
158 | WorkingDirectory = Path.Combine(FPM.SourcePath, "Launcher")
159 | }}.Start();
160 | }
161 | }
162 |
163 | public void UpdateList_ItemChecked(object sender, ItemCheckedEventArgs e)
164 | {
165 | if (!FPM.Ready || !UpdateList.Focused) return;
166 |
167 | var tagData = (dynamic)e.Item.Tag;
168 | var component = tagData.Component as Common.Component;
169 | long sizeChange = tagData.SizeChange;
170 |
171 | if (component != null) {
172 | if (e.Item.Checked)
173 | {
174 | FPM.totalSizeChange += sizeChange;
175 | }
176 | else if (!e.Item.Checked)
177 | {
178 | FPM.totalSizeChange -= sizeChange;
179 | }
180 |
181 | component.Checked2 = e.Item.Checked;
182 |
183 | numToUpdateLabel.Text = $"Total updates: {UpdateList.CheckedItems.Count}";
184 | numUpdateSizeLabel.Text = $"Total size: {FPM.GetFormattedBytes(FPM.totalSizeChange, true)}";
185 | }
186 |
187 | UpdateButton.Enabled = UpdateList.CheckedItems.Count > 0;
188 | }
189 |
190 | private void chkUncheckAll_CheckedChanged(object sender, EventArgs e)
191 | {
192 | UpdateList.Focus();
193 |
194 | foreach (ListViewItem item in UpdateList.Items)
195 | {
196 | item.Checked = chkUncheckAll.Checked;
197 | }
198 |
199 | UpdateButton.Enabled = chkUncheckAll.Checked;
200 |
201 | ChangeButton.Focus();
202 | }
203 |
204 | private void tabControl_SelectedIndexChanged(object sender, EventArgs e)
205 | {
206 | string selectedTab = TabControl.SelectedTab.Name;
207 |
208 | SetAllStatusLabelsVisibility(false);
209 |
210 | if (selectedTab == "ManageTab")
211 | {
212 | ShowStatusLabels("installSizeLabel");
213 | ShowStatusLabels("numInstalledLabel");
214 | ShowStatusLabels("numMissingLabel");
215 |
216 | }
217 | else if (selectedTab == "UpdateTab")
218 | {
219 | ShowStatusLabels("numToUpdateLabel", "numUpdateSizeLabel");
220 | }
221 | }
222 |
223 | private void SetAllStatusLabelsVisibility(bool visible)
224 | {
225 | foreach (var item in statusStrip1.Items)
226 | {
227 | if (item is ToolStripStatusLabel label)
228 | {
229 | label.Visible = visible;
230 | }
231 | }
232 | }
233 |
234 | private void ShowStatusLabels(params string[] labelNames)
235 | {
236 | foreach (string name in labelNames)
237 | {
238 | var label = statusStrip1.Items.Find(name, false).FirstOrDefault() as ToolStripStatusLabel;
239 | if (label != null)
240 | {
241 | label.Visible = true;
242 | }
243 | }
244 | }
245 |
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/FlashpointManager/src/Forms/Operate.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace FlashpointManager
2 | {
3 | partial class Operate
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.ProgressMeasure = new System.Windows.Forms.ProgressBar();
32 | this.ProgressLabel = new System.Windows.Forms.Label();
33 | this.CancelButton = new System.Windows.Forms.Button();
34 | this.SuspendLayout();
35 | //
36 | // ProgressMeasure
37 | //
38 | this.ProgressMeasure.Location = new System.Drawing.Point(12, 10);
39 | this.ProgressMeasure.Maximum = 1000;
40 | this.ProgressMeasure.Name = "ProgressMeasure";
41 | this.ProgressMeasure.Size = new System.Drawing.Size(472, 23);
42 | this.ProgressMeasure.TabIndex = 0;
43 | //
44 | // ProgressLabel
45 | //
46 | this.ProgressLabel.AutoSize = true;
47 | this.ProgressLabel.Location = new System.Drawing.Point(9, 46);
48 | this.ProgressLabel.Name = "ProgressLabel";
49 | this.ProgressLabel.Size = new System.Drawing.Size(61, 13);
50 | this.ProgressLabel.TabIndex = 1;
51 | this.ProgressLabel.Text = "Preparing...";
52 | //
53 | // CancelButton
54 | //
55 | this.CancelButton.Location = new System.Drawing.Point(410, 41);
56 | this.CancelButton.Name = "CancelButton";
57 | this.CancelButton.Size = new System.Drawing.Size(75, 23);
58 | this.CancelButton.TabIndex = 2;
59 | this.CancelButton.Text = "&Cancel";
60 | this.CancelButton.UseVisualStyleBackColor = true;
61 | this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click);
62 | //
63 | // Operate
64 | //
65 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
66 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
67 | this.ClientSize = new System.Drawing.Size(496, 73);
68 | this.ControlBox = false;
69 | this.Controls.Add(this.CancelButton);
70 | this.Controls.Add(this.ProgressLabel);
71 | this.Controls.Add(this.ProgressMeasure);
72 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
73 | this.MaximizeBox = false;
74 | this.MinimizeBox = false;
75 | this.Name = "Operate";
76 | this.ShowInTaskbar = false;
77 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
78 | this.Text = "Modifying Flashpoint...";
79 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Operation_FormClosing);
80 | this.Load += new System.EventHandler(this.Operation_Load);
81 | this.ResumeLayout(false);
82 | this.PerformLayout();
83 |
84 | }
85 |
86 | #endregion
87 | private System.Windows.Forms.ProgressBar ProgressMeasure;
88 | private System.Windows.Forms.Label ProgressLabel;
89 | private System.Windows.Forms.Button CancelButton;
90 | }
91 | }
--------------------------------------------------------------------------------
/FlashpointManager/src/Forms/Operate.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Net;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 | using System.Windows.Forms;
10 |
11 | using FlashpointManager.Common;
12 | using Microsoft.WindowsAPICodePack.Taskbar;
13 | using SharpCompress.Archives.Zip;
14 | using SharpCompress.Common;
15 | using SharpCompress.Readers;
16 |
17 | namespace FlashpointManager
18 | {
19 | public partial class Operate : Form
20 | {
21 | Component workingComponent;
22 |
23 | List addedComponents = new List();
24 | List removedComponents = new List();
25 |
26 | Stream stream;
27 | ZipArchive archive;
28 | IReader reader;
29 |
30 | long byteProgress = 0;
31 | long byteTotal = 0;
32 |
33 | int cancelStatus = 0;
34 |
35 | private CancellationTokenSource cancellationTokenSource;
36 | private IProgress progressReporter;
37 |
38 | public Operate()
39 | {
40 | InitializeComponent();
41 | cancellationTokenSource = new CancellationTokenSource();
42 | progressReporter = new Progress(ReportProgress);
43 |
44 | //FPM.Client.Proxy = new WebProxy("http://127.0.0.1:8888");
45 | }
46 |
47 | private async void Operation_Load(object sender, EventArgs e)
48 | {
49 | var token = cancellationTokenSource.Token;
50 |
51 | TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Normal, FPM.Main.Handle);
52 |
53 | FPM.Client.DownloadProgressChanged += OnDownloadProgressChanged;
54 |
55 | if (FPM.OperationMode == OperateMode.Modify)
56 | {
57 | FPM.IterateList(FPM.Main.ComponentList.Nodes, node =>
58 | {
59 | if (node.Tag.GetType().ToString().EndsWith("Component"))
60 | {
61 | var component = node.Tag as Component;
62 |
63 | if (!node.Checked && FPM.ComponentTracker.Downloaded.Exists(c => c.ID == component.ID))
64 | {
65 | removedComponents.Add(component);
66 | }
67 | if (node.Checked && !FPM.ComponentTracker.Downloaded.Exists(c => c.ID == component.ID))
68 | {
69 | addedComponents.Add(component);
70 | }
71 | }
72 | });
73 | }
74 | else if (FPM.OperationMode == OperateMode.Update)
75 | {
76 | foreach (var component in FPM.ComponentTracker.Outdated)
77 | {
78 | if (!component.Checked2) continue;
79 | if (FPM.ComponentTracker.Downloaded.Exists(c => c.ID == component.ID))
80 | {
81 | removedComponents.Add(component);
82 | }
83 |
84 | addedComponents.Add(component);
85 | }
86 |
87 | removedComponents.AddRange(FPM.ComponentTracker.Deprecated.Where(c => c.Checked2));
88 | }
89 | else
90 | {
91 | foreach (var component in FPM.ComponentTracker.Broken)
92 | {
93 | removedComponents.Add(component);
94 | addedComponents.Add(component);
95 | }
96 | }
97 |
98 | byteTotal = FPM.OperationMode != OperateMode.Modify
99 | ? addedComponents.Sum(c => c.Size)
100 | : addedComponents.Concat(removedComponents).Sum(c => c.Size);
101 |
102 | foreach (var component in addedComponents)
103 | {
104 | workingComponent = component;
105 |
106 | if (component.Size > 0)
107 | {
108 | while (true)
109 | {
110 | try
111 | {
112 | using (var ctr = token.Register(() => FPM.Client.CancelAsync()))
113 | {
114 | stream = new MemoryStream(await FPM.Client.DownloadDataTaskAsync(component.URL));
115 | }
116 | }
117 | catch(WebException ex) when (ex.Status == WebExceptionStatus.RequestCanceled)
118 | {
119 | cancelStatus = 2;
120 | return;
121 |
122 | }
123 | catch (WebException ex)
124 | {
125 | var errorResult = MessageBox.Show(
126 | $"The {workingComponent.Title} component failed to download.\n\n" +
127 | "Click OK to retry, or Cancel to abort the installation.",
128 | "Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Error
129 | );
130 |
131 | if (errorResult == DialogResult.OK)
132 | {
133 | // Retry the download
134 | continue;
135 | }
136 | else
137 | {
138 | cancelStatus = 2;
139 | cancellationTokenSource.Cancel();
140 | return;
141 | }
142 | }
143 | break;
144 | }
145 | }
146 |
147 | await Task.Run(() => ExtractComponents(cancellationTokenSource.Token, progressReporter), cancellationTokenSource.Token);
148 |
149 | byteProgress += component.Size;
150 | }
151 |
152 | if (cancelStatus != 0) return;
153 | CancelButton.Enabled = false;
154 |
155 | foreach (var component in removedComponents)
156 | {
157 | workingComponent = component;
158 |
159 | await Task.Run(() => RemoveComponents(cancellationTokenSource.Token, progressReporter), cancellationTokenSource.Token);
160 |
161 | byteProgress += component.Size;
162 | }
163 |
164 | ProgressLabel.Text = "[100%] Finishing up...";
165 |
166 | foreach (var component in addedComponents)
167 | {
168 | workingComponent = component;
169 |
170 | await Task.Run(() => ApplyComponents(cancellationTokenSource.Token), cancellationTokenSource.Token);
171 | }
172 |
173 | TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress, FPM.Main.Handle);
174 |
175 | if (FPM.AutoDownload.Count == 0) FPM.SyncManager();
176 |
177 | cancelStatus = 2;
178 | Close();
179 | }
180 |
181 | private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
182 | {
183 | if (cancelStatus != 0)
184 | {
185 | FPM.Client.CancelAsync();
186 | return;
187 | }
188 |
189 | double currentProgress = (double)e.BytesReceived / e.TotalBytesToReceive;
190 | long currentSize = workingComponent.Size;
191 | double totalProgress = (byteProgress + (currentProgress / 2 * currentSize)) / byteTotal;
192 |
193 | ProgressMeasure.Invoke((MethodInvoker)delegate
194 | {
195 | ProgressMeasure.Value = (int)((double)totalProgress * ProgressMeasure.Maximum);
196 | });
197 |
198 | ProgressLabel.Invoke((MethodInvoker)delegate
199 | {
200 | ProgressLabel.Text =
201 | $"[{(int)((double)totalProgress * 100)}%] Downloading component \"{workingComponent.Title}\"... " +
202 | $"{FPM.GetFormattedBytes(e.BytesReceived)} of {FPM.GetFormattedBytes(e.TotalBytesToReceive)}";
203 | });
204 |
205 | FPM.Main.Invoke((MethodInvoker)delegate
206 | {
207 | TaskbarManager.Instance.SetProgressValue(
208 | (int)((double)totalProgress * ProgressMeasure.Maximum), ProgressMeasure.Maximum, FPM.Main.Handle
209 | );
210 | });
211 | }
212 |
213 | private void ExtractComponents(CancellationToken cancellationToken, IProgress progress)
214 | {
215 | string rootPath = Path.Combine(FPM.SourcePath, "Temp");
216 | string infoPath = Path.Combine(rootPath, "Components");
217 | string infoFile = Path.Combine(infoPath, workingComponent.ID);
218 |
219 | Directory.CreateDirectory(infoPath);
220 |
221 | using (TextWriter writer = File.CreateText(infoFile))
222 | {
223 | string[] header = new List
224 | { workingComponent.Hash, $"{workingComponent.Size}" }.Concat(workingComponent.Depends).ToArray();
225 |
226 | writer.WriteLine(string.Join(" ", header));
227 | }
228 |
229 | if (workingComponent.Size == 0) return;
230 | try
231 | {
232 | using (archive = ZipArchive.Open(stream))
233 | {
234 | using (reader = archive.ExtractAllEntries())
235 | {
236 | long extractedSize = 0;
237 | long totalSize = archive.TotalUncompressSize;
238 |
239 | string destPath = Path.Combine(rootPath, workingComponent.Path.Replace('/', '\\'));
240 |
241 | while (reader.MoveToNextEntry())
242 | {
243 | cancellationToken.ThrowIfCancellationRequested();
244 |
245 | if (reader.Entry.IsDirectory) continue;
246 |
247 | Directory.CreateDirectory(destPath);
248 |
249 | reader.WriteEntryToDirectory(destPath, new ExtractionOptions {
250 | ExtractFullPath = true, Overwrite = true, PreserveFileTime = true
251 | });
252 |
253 | using (TextWriter writer = File.AppendText(infoFile))
254 | {
255 | writer.WriteLine(Path.Combine(workingComponent.Path, reader.Entry.Key).Replace("/", @"\"));
256 | }
257 |
258 | extractedSize += reader.Entry.Size;
259 |
260 | double currentProgress = (double)extractedSize / totalSize;
261 | long currentSize = workingComponent.Size;
262 | double totalProgress = (byteProgress + (currentSize / 2) + (currentProgress / 2 * currentSize)) / byteTotal;
263 |
264 | progress.Report(totalProgress);
265 |
266 | ProgressLabel.Invoke((MethodInvoker)delegate
267 | {
268 | ProgressLabel.Text =
269 | $"[{(int)((double)totalProgress * 100)}%] Extracting component \"{workingComponent.Title}\"... " +
270 | $"{FPM.GetFormattedBytes(extractedSize)} of {FPM.GetFormattedBytes(totalSize)}";
271 | });
272 |
273 | FPM.Main.Invoke((MethodInvoker)delegate
274 | {
275 | TaskbarManager.Instance.SetProgressValue(
276 | (int)((double)totalProgress * ProgressMeasure.Maximum), ProgressMeasure.Maximum, FPM.Main.Handle
277 | );
278 | });
279 | }
280 | }
281 | }
282 | }
283 | catch (OperationCanceledException)
284 | {
285 | cancelStatus = 2;
286 | }
287 | }
288 |
289 | private void RemoveComponents(CancellationToken cancellationToken, IProgress progress)
290 | {
291 | string[] infoText = File.ReadLines(workingComponent.InfoFile).Skip(1).ToArray();
292 |
293 | long removedFiles = 0;
294 | long totalFiles = infoText.Length;
295 | long totalSize = workingComponent.Size;
296 |
297 | foreach (string file in infoText)
298 | {
299 | FPM.DeleteFileAndDirectories(Path.Combine(FPM.SourcePath, file));
300 | removedFiles++;
301 |
302 | double removeProgress = (double)removedFiles / totalFiles;
303 | double totalProgress = FPM.OperationMode != OperateMode.Modify ? 1 : (byteProgress + (removeProgress * totalSize)) / byteTotal;
304 |
305 | progress.Report(totalProgress);
306 |
307 | ProgressLabel.Invoke((MethodInvoker)delegate
308 | {
309 | string text = FPM.ComponentTracker.Outdated.Exists(c => c.ID == workingComponent.ID)
310 | ? $"Removing old version of component \"{workingComponent.Title}\"..."
311 | : $"Removing component \"{workingComponent.Title}\"...";
312 |
313 | ProgressLabel.Text = $"[{(int)((double)totalProgress * 100)}%] {text} {removedFiles} of {totalFiles} files";
314 | });
315 |
316 | FPM.Main.Invoke((MethodInvoker)delegate
317 | {
318 | TaskbarManager.Instance.SetProgressValue(
319 | (int)((double)totalProgress * ProgressMeasure.Maximum), ProgressMeasure.Maximum, FPM.Main.Handle
320 | );
321 | });
322 | }
323 |
324 | FPM.DeleteFileAndDirectories(workingComponent.InfoFile);
325 | }
326 |
327 | private void ApplyComponents(CancellationToken cancellationToken)
328 | {
329 | void MoveDelete(string source, string dest)
330 | {
331 | try
332 | {
333 | File.Move(source, dest);
334 | FPM.DeleteFileAndDirectories(source);
335 | }
336 | catch
337 | {
338 | FPM.GenericError(
339 | "Failed to move the following file:\n" + source + "\n\n" +
340 | "You will have to move it manually from the Temp folder."
341 | );
342 | }
343 | }
344 |
345 | string tempPath = Path.Combine(FPM.SourcePath, "Temp");
346 | string tempInfoFile = Path.Combine(tempPath, "Components", workingComponent.ID);
347 | string[] infoText = File.ReadLines(tempInfoFile).Skip(1).ToArray();
348 |
349 | foreach (string file in infoText)
350 | {
351 | string tempFile = Path.Combine(FPM.SourcePath, "Temp", file);
352 | string destFile = Path.Combine(FPM.SourcePath, file);
353 |
354 | Directory.CreateDirectory(Path.GetDirectoryName(destFile));
355 |
356 | MoveDelete(tempFile, destFile);
357 |
358 | if (cancellationToken.IsCancellationRequested)
359 | {
360 | cancelStatus = 2;
361 | break;
362 | }
363 | }
364 |
365 | MoveDelete(tempInfoFile, workingComponent.InfoFile);
366 | }
367 |
368 | private async void CancelButton_Click(object sender, EventArgs e)
369 | {
370 | if (cancelStatus < 1) cancelStatus = 1;
371 |
372 | CancelButton.Enabled = false;
373 | ProgressLabel.Invoke((MethodInvoker)delegate
374 | {
375 | ProgressLabel.Text = "Cancelling...";
376 | });
377 |
378 | cancellationTokenSource.Cancel();
379 |
380 | await Task.Run(() =>
381 | {
382 | while (cancelStatus != 2) { }
383 |
384 | string tempPath = Path.Combine(FPM.SourcePath, "Temp");
385 |
386 | if (Directory.Exists(tempPath))
387 | {
388 | foreach (string tempFile in Directory.EnumerateFiles(tempPath))
389 | {
390 | try { File.Delete(tempFile); } catch { }
391 | }
392 |
393 | try { Directory.Delete(tempPath, true); } catch { }
394 | }
395 | });
396 |
397 | TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress, FPM.Main.Handle);
398 | Close();
399 | }
400 |
401 | private void Operation_FormClosing(object sender, FormClosingEventArgs e)
402 | {
403 | if (cancelStatus != 2) e.Cancel = true;
404 |
405 | FPM.Client.DownloadProgressChanged -= OnDownloadProgressChanged;
406 | }
407 |
408 | private void ReportProgress(double value)
409 | {
410 | ProgressMeasure.Invoke((MethodInvoker)delegate
411 | {
412 | ProgressMeasure.Value = (int)(value * ProgressMeasure.Maximum);
413 | });
414 | }
415 | }
416 | }
--------------------------------------------------------------------------------
/FlashpointManager/src/Forms/Operate.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/FlashpointManager/src/Forms/Settings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Threading.Tasks;
5 | using System.Windows.Forms;
6 |
7 | using FlashpointManager.Common;
8 | using FlashpointManager.src.Forms;
9 | using Microsoft.WindowsAPICodePack.Dialogs;
10 |
11 | namespace FlashpointManager
12 | {
13 | public partial class Main : Form
14 | {
15 | private void BrowseButton_Click(object sender, EventArgs e)
16 | {
17 | var pathDialog = new CommonOpenFileDialog() { IsFolderPicker = true };
18 |
19 | if (pathDialog.ShowDialog() == CommonFileDialogResult.Ok)
20 | {
21 | if (FPM.VerifySourcePath(pathDialog.FileName))
22 | {
23 | LocationBox.Text = pathDialog.FileName;
24 | }
25 | else
26 | {
27 | FPM.GenericError("The specified Flashpoint directory is invalid!");
28 | }
29 | }
30 | }
31 |
32 | private void SaveButton_Click(object sender, EventArgs e)
33 | {
34 | if (!FPM.WriteConfig(LocationBox.Text, RepositoryBox.Text)) return;
35 |
36 | Application.Restart();
37 | }
38 |
39 | private void CheckFilesButton_Click(object sender, EventArgs e) => new CheckFiles().ShowDialog();
40 |
41 | private void ComponentRepo_CheckedChanged(object sender, EventArgs e)
42 | {
43 | var radio = sender as RadioButton;
44 | if (!radio.Checked) return;
45 |
46 | if (radio.Name == "StableRepo")
47 | {
48 | RepositoryBox.Text = FPM.RepoXmlTemplates.Stable;
49 | }
50 | else if (radio.Name == "DevRepo")
51 | {
52 | RepositoryBox.Text = FPM.RepoXmlTemplates.Development;
53 | }
54 | else
55 | {
56 | RepositoryBox.Text = FPM.RepoXml;
57 | }
58 |
59 | RepositoryBox.Enabled = radio.Name == "CustomRepo";
60 | }
61 |
62 | private async void UninstallButton_Click(object sender, EventArgs e)
63 | {
64 | var uninstallDialog = MessageBox.Show(
65 | "Understand that uninstalling Flashpoint will delete:\n\n" +
66 | "- Downloaded entries\n" +
67 | "- Save data (depending on the entry)\n" +
68 | "- Playlists\n" +
69 | "- Favorites\n\n" +
70 | "This operation cannot be cancelled once started. Are you sure you want to continue?",
71 | "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning
72 | );
73 |
74 | if (uninstallDialog != DialogResult.Yes) return;
75 |
76 | TabControl.Enabled = false;
77 |
78 | await Task.Run(() => {
79 | foreach (string file in Directory.EnumerateFiles(FPM.SourcePath, "*", SearchOption.AllDirectories))
80 | {
81 | try { FPM.DeleteFileAndDirectories(file); } catch { }
82 | }
83 |
84 | var shortcutPaths = new string[]
85 | {
86 | Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Programs), "Flashpoint.lnk"),
87 | Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Flashpoint.lnk"),
88 | Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Flashpoint.lnk")
89 | };
90 |
91 | foreach (string path in shortcutPaths)
92 | {
93 | try { File.Delete(path); } catch { }
94 | }
95 | });
96 |
97 | MessageBox.Show(
98 | "Flashpoint has been uninstalled from your system.",
99 | "Uninstallation Complete", MessageBoxButtons.OK, MessageBoxIcon.Information
100 | );
101 |
102 | new Process()
103 | {
104 | StartInfo = {
105 | FileName = "cmd.exe",
106 | Arguments = "/k timeout /nobreak /t 1 >nul & " +
107 | $"rmdir /Q \"{Path.GetFullPath(Path.Combine(FPM.SourcePath, "Manager"))}\" & " +
108 | $"rmdir /Q \"{Path.GetFullPath(FPM.SourcePath)}\" & exit",
109 | WorkingDirectory = Path.GetFullPath(Path.Combine(FPM.SourcePath, "..")),
110 | WindowStyle = ProcessWindowStyle.Hidden
111 | }
112 | }.Start();
113 |
114 | Close();
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/FlashpointManager/src/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Windows.Forms;
7 | using System.Xml;
8 |
9 | using FlashpointManager.Common;
10 | using Microsoft.WindowsAPICodePack.Dialogs;
11 |
12 | namespace FlashpointManager
13 | {
14 | internal static class Program
15 | {
16 | ///
17 | /// The main entry point for the application.
18 | ///
19 | [STAThread]
20 | static void Main(string[] args)
21 | {
22 | // Run app from temp folder as long as /notemp argument isn't passed
23 | // This allows the executable to be deleted when updating its component or uninstalling Flashpoint
24 | if (!args.Any(v => v.ToLower() == "/notemp") && !Debugger.IsAttached)
25 | {
26 | string realPath = AppDomain.CurrentDomain.BaseDirectory;
27 | string realFile = AppDomain.CurrentDomain.FriendlyName;
28 | string tempPath = Path.GetTempPath();
29 | string tempFile = $"69McIKvK_{realFile}";
30 |
31 | if (realPath != tempPath && realFile != tempFile)
32 | {
33 | File.Copy(realPath + realFile, tempPath + tempFile, true);
34 | Process.Start(new ProcessStartInfo
35 | {
36 | FileName = tempPath + tempFile,
37 | Arguments = string.Join(" ", args),
38 | WorkingDirectory = realPath
39 | });
40 | Environment.Exit(0);
41 | }
42 | else if (tempPath.TrimEnd('\\') == Directory.GetCurrentDirectory())
43 | {
44 | Environment.Exit(0);
45 | }
46 | }
47 |
48 | Application.EnableVisualStyles();
49 | Application.SetCompatibleTextRenderingDefault(false);
50 |
51 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
52 |
53 | // Load config, or create if it doesn't exist
54 | try
55 | {
56 | var configReader = File.ReadAllLines(FPM.ConfigFile);
57 | FPM.SourcePath = configReader[0];
58 | FPM.RepoXml = configReader[1];
59 | }
60 | catch
61 | {
62 | using (var configWriter = File.CreateText(FPM.ConfigFile))
63 | {
64 | configWriter.WriteLine(FPM.SourcePath);
65 | configWriter.WriteLine(FPM.RepoXml);
66 | }
67 | }
68 |
69 | Stream listStream = null;
70 |
71 | // Attempt to download and parse component list
72 | try
73 | {
74 | listStream = new MemoryStream(FPM.Client.DownloadData(FPM.RepoXml));
75 | FPM.XmlTree.Load(listStream);
76 | }
77 | catch (Exception e)
78 | {
79 | string failedAction = e is XmlException ? "parsed" : "downloaded";
80 |
81 | FPM.GenericError(
82 | $"The component list could not be {failedAction}! An offline backup will be used instead.\n\n" +
83 | "Verify that your internet connection is working and that your component source is not misconfigured."
84 | );
85 |
86 | FPM.OfflineMode = true;
87 | }
88 |
89 | string backupPath = Path.Combine(FPM.SourcePath, "Components", "components.bak");
90 |
91 | // Create backup if component list was successfully downloaded and parsed; otherwise, load from backup
92 | if (!FPM.OfflineMode)
93 | {
94 | try
95 | {
96 | using (var fileStream = new FileStream(backupPath, FileMode.OpenOrCreate, FileAccess.Write))
97 | {
98 | fileStream.SetLength(0);
99 | listStream.Position = 0;
100 | listStream.CopyTo(fileStream);
101 | }
102 | }
103 | catch { }
104 | }
105 | else
106 | {
107 | try
108 | {
109 | listStream = new FileStream(backupPath, FileMode.Open, FileAccess.Read);
110 | FPM.XmlTree.Load(listStream);
111 | }
112 | catch
113 | {
114 | FPM.GenericError("Failed to load component list from offline backup!");
115 | Environment.Exit(1);
116 | }
117 | }
118 |
119 | // Verify that the configured Flashpoint path is valid
120 | if (!FPM.VerifySourcePath(FPM.SourcePath))
121 | {
122 | string newPath = FPM.SourcePath;
123 | string parentPath = Directory.GetParent(Directory.GetCurrentDirectory()).FullName;
124 |
125 | // If not, check if the parent directory is a valid Flashpoint path and use that if so
126 | if (FPM.VerifySourcePath(parentPath))
127 | {
128 | newPath = parentPath;
129 | }
130 | // If neither are valid, ask the user to choose a valid directory
131 | else
132 | {
133 | while (!FPM.VerifySourcePath(newPath))
134 | {
135 | FPM.GenericError(
136 | "The Flashpoint directory specified in fpm.cfg is invalid!\n\n" +
137 | "Please choose a valid directory."
138 | );
139 |
140 | var pathDialog = new CommonOpenFileDialog() { IsFolderPicker = true };
141 |
142 | if (pathDialog.ShowDialog() == CommonFileDialogResult.Cancel)
143 | {
144 | Environment.Exit(1);
145 | }
146 |
147 | newPath = pathDialog.FileName;
148 | }
149 | }
150 |
151 | // Write the new Flashpoint path to fpm.cfg
152 | if (!FPM.WriteConfig(newPath, FPM.RepoXml))
153 | {
154 | Environment.Exit(1);
155 | }
156 |
157 | FPM.SourcePath = newPath;
158 | }
159 |
160 | if (args.Length > 0)
161 | {
162 | // Open update tab on startup if /update argument is passed
163 | if (args.Any(v => v.ToLower() == "/update"))
164 | {
165 | FPM.OpenUpdateTab = true;
166 | }
167 |
168 | // Open launcher on close if /launcher argument is passed
169 | if (args.Any(v => v.ToLower() == "/launcher"))
170 | {
171 | FPM.OpenLauncherOnClose = true;
172 | }
173 |
174 | // Automatically download components if /download argument is passed
175 | var argsList = args.ToList();
176 | int first = argsList.FindIndex(v => v.ToLower() == "/download");
177 |
178 | if (first > -1 && first < argsList.Count - 1)
179 | {
180 | int last = argsList.FindIndex(first + 1, v => v.StartsWith("/"));
181 | if (last == -1) last = argsList.Count;
182 |
183 | FPM.AutoDownload.AddRange(argsList.Skip(first + 1).Take(last - first - 1));
184 | }
185 | }
186 |
187 | // Display the application window
188 | Application.Run(new Main() { Opacity = FPM.AutoDownload.Count == 0 ? 1 : 0 });
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/FlashpointManager/src/TriStateTreeView.cs:
--------------------------------------------------------------------------------
1 | // Copyright (CPOL) 2011 RikTheVeggie - see http://www.codeproject.com/info/cpol10.aspx
2 | // Tri-State Tree View http://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=202435
3 |
4 | namespace RikTheVeggie
5 | {
6 | //
7 | // A Tri-State TreeView designed for on-demand populating of the tree
8 | //
9 | //
10 | // 'Mixed' nodes retain their checked state, meaning they can be checked or unchecked according to their current state
11 | // Tree can be navigated by keyboard (cursor keys & space)
12 | // No need to do anything special in calling code
13 | //
14 | public class TriStateTreeView : System.Windows.Forms.TreeView
15 | {
16 | //
17 | // CheckedState is an enum of all allowable nodes states
18 | //
19 | public enum CheckedState : int { UnInitialised = -1, UnChecked, Checked, Mixed };
20 |
21 | //
22 | // IgnoreClickAction is used to ingore messages generated by setting the node.Checked flag in code
23 | // Do not set e.Cancel = true in OnBeforeCheck otherwise the Checked state will be lost
24 | //
25 | int IgnoreClickAction = 0;
26 | //
27 |
28 | // TriStateStyles is an enum of all allowable tree styles
29 | // All styles check children when parent is checked
30 | // Installer automatically checks parent if all children are checked, and unchecks parent if at least one child is unchecked
31 | // Standard never changes the checked status of a parent
32 | //
33 | public enum TriStateStyles : int { Standard = 0, Installer };
34 |
35 | // Create a private member for the tree style, and allow it to be set on the property sheer
36 | private TriStateStyles TriStateStyle = TriStateStyles.Standard;
37 |
38 | [System.ComponentModel.Category("Tri-State Tree View")]
39 | [System.ComponentModel.DisplayName("Style")]
40 | [System.ComponentModel.Description("Style of the Tri-State Tree View")]
41 | public TriStateStyles TriStateStyleProperty
42 | {
43 | get { return TriStateStyle; }
44 | set { TriStateStyle = value; }
45 | }
46 |
47 | //
48 | // Constructor. Create and populate an image list
49 | //
50 | public TriStateTreeView() : base()
51 | {
52 | StateImageList = new System.Windows.Forms.ImageList();
53 |
54 | // populate the image list, using images from the System.Windows.Forms.CheckBoxRenderer class
55 | for (int i = 0; i < 3; i++)
56 | {
57 | // Create a bitmap which holds the relevent check box style
58 | // see http://msdn.microsoft.com/en-us/library/ms404307.aspx and http://msdn.microsoft.com/en-us/library/system.windows.forms.checkboxrenderer.aspx
59 |
60 | System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(16, 16);
61 | System.Drawing.Graphics chkGraphics = System.Drawing.Graphics.FromImage(bmp);
62 | switch ( i )
63 | {
64 | // 0,1 - offset the checkbox slightly so it positions in the correct place
65 | case 0:
66 | System.Windows.Forms.CheckBoxRenderer.DrawCheckBox(chkGraphics, new System.Drawing.Point(0, 1), System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);
67 | break;
68 | case 1:
69 | System.Windows.Forms.CheckBoxRenderer.DrawCheckBox(chkGraphics, new System.Drawing.Point(0, 1), System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal);
70 | break;
71 | case 2:
72 | System.Windows.Forms.CheckBoxRenderer.DrawCheckBox(chkGraphics, new System.Drawing.Point(0, 1), System.Windows.Forms.VisualStyles.CheckBoxState.MixedNormal);
73 | break;
74 | }
75 |
76 | StateImageList.Images.Add(bmp);
77 | }
78 | }
79 |
80 | //
81 | // Called once before window displayed. Disables default Checkbox functionality and ensures all nodes display an 'unchecked' image.
82 | //
83 | protected override void OnCreateControl()
84 | {
85 | base.OnCreateControl();
86 | CheckBoxes = false; // Disable default CheckBox functionality if it's been enabled
87 |
88 | // Give every node an initial 'unchecked' image
89 | IgnoreClickAction++; // we're making changes to the tree, ignore any other change requests
90 | UpdateChildState(this.Nodes, (int)CheckedState.UnChecked, false, true);
91 | IgnoreClickAction--;
92 | }
93 |
94 | //
95 | // Called after a node is checked. Forces all children to inherit current state, and notifies parents they may need to become 'mixed'
96 | //
97 | protected override void OnAfterCheck(System.Windows.Forms.TreeViewEventArgs e)
98 | {
99 | base.OnAfterCheck(e);
100 |
101 | if (IgnoreClickAction > 0)
102 | {
103 | return;
104 | }
105 |
106 | IgnoreClickAction++; // we're making changes to the tree, ignore any other change requests
107 |
108 | // the checked state has already been changed, we just need to update the state index
109 |
110 | // node is either ticked or unticked. ignore mixed state, as the node is still only ticked or unticked regardless of state of children
111 | System.Windows.Forms.TreeNode tn = e.Node;
112 | tn.StateImageIndex = tn.Checked ? (int)CheckedState.Checked : (int)CheckedState.UnChecked;
113 |
114 | // force all children to inherit the same state as the current node
115 | UpdateChildState(e.Node.Nodes, e.Node.StateImageIndex, e.Node.Checked, false);
116 |
117 | // populate state up the tree, possibly resulting in parents with mixed state
118 | UpdateParentState(e.Node.Parent);
119 |
120 | IgnoreClickAction--;
121 | }
122 |
123 | //
124 | // Called after a node is expanded. Ensures any new nodes display an 'unchecked' image
125 | //
126 | protected override void OnAfterExpand(System.Windows.Forms.TreeViewEventArgs e)
127 | {
128 | // If any child node is new, give it the same check state as the current node
129 | // So if current node is ticked, child nodes will also be ticked
130 | base.OnAfterExpand(e);
131 |
132 | IgnoreClickAction++; // we're making changes to the tree, ignore any other change requests
133 | UpdateChildState(e.Node.Nodes, e.Node.StateImageIndex, e.Node.Checked, true);
134 | IgnoreClickAction--;
135 | }
136 |
137 | //
138 | // Helper function to replace child state with that of the parent
139 | //
140 | protected void UpdateChildState(System.Windows.Forms.TreeNodeCollection Nodes, int StateImageIndex, bool Checked, bool ChangeUninitialisedNodesOnly)
141 | {
142 | foreach (System.Windows.Forms.TreeNode tnChild in Nodes)
143 | {
144 | if (!ChangeUninitialisedNodesOnly || tnChild.StateImageIndex == -1)
145 | {
146 | tnChild.StateImageIndex = StateImageIndex;
147 | tnChild.Checked = Checked; // override 'checked' state of child with that of parent
148 |
149 | if (tnChild.Nodes.Count > 0)
150 | {
151 | UpdateChildState(tnChild.Nodes, StateImageIndex, Checked, ChangeUninitialisedNodesOnly);
152 | }
153 | }
154 | }
155 | }
156 |
157 | //
158 | // Helper function to notify parent it may need to use 'mixed' state
159 | //
160 | protected void UpdateParentState(System.Windows.Forms.TreeNode tn)
161 | {
162 | // Node needs to check all of it's children to see if any of them are ticked or mixed
163 | if (tn == null)
164 | return;
165 |
166 | int OrigStateImageIndex = tn.StateImageIndex;
167 |
168 | int UnCheckedNodes = 0, CheckedNodes = 0, MixedNodes = 0;
169 |
170 | // The parent needs to know how many of it's children are Checked or Mixed
171 | foreach (System.Windows.Forms.TreeNode tnChild in tn.Nodes)
172 | {
173 | if (tnChild.StateImageIndex == (int)CheckedState.Checked)
174 | CheckedNodes++;
175 | else if (tnChild.StateImageIndex == (int)CheckedState.Mixed)
176 | {
177 | MixedNodes++;
178 | break;
179 | }
180 | else
181 | UnCheckedNodes++;
182 | }
183 |
184 | if (TriStateStyle == TriStateStyles.Installer)
185 | {
186 | // In Installer mode, if all child nodes are checked then parent is checked
187 | // If at least one child is unchecked, then parent is unchecked
188 | if (MixedNodes == 0)
189 | {
190 | if (UnCheckedNodes == 0)
191 | {
192 | // all children are checked, so parent must be checked
193 | tn.Checked = true;
194 | }
195 | else
196 | {
197 | // at least one child is unchecked, so parent must be unchecked
198 | tn.Checked = false;
199 | }
200 | }
201 | }
202 |
203 | // Determine the parent's new Image State
204 | if (MixedNodes > 0)
205 | {
206 | // at least one child is mixed, so parent must be mixed
207 | tn.StateImageIndex = (int)CheckedState.Mixed;
208 | }
209 | else if (CheckedNodes > 0 && UnCheckedNodes == 0)
210 | {
211 | // all children are checked
212 | if (tn.Checked)
213 | tn.StateImageIndex = (int)CheckedState.Checked;
214 | else
215 | tn.StateImageIndex = (int)CheckedState.Mixed;
216 | }
217 | else if (CheckedNodes > 0)
218 | {
219 | // some children are checked, the rest are unchecked
220 | tn.StateImageIndex = (int)CheckedState.Mixed;
221 | }
222 | else
223 | {
224 | // all children are unchecked
225 | if (tn.Checked)
226 | tn.StateImageIndex = (int)CheckedState.Mixed;
227 | else
228 | tn.StateImageIndex = (int)CheckedState.UnChecked;
229 | }
230 |
231 | if (OrigStateImageIndex != tn.StateImageIndex && tn.Parent != null)
232 | {
233 | // Parent's state has changed, notify the parent's parent
234 | UpdateParentState(tn.Parent);
235 | }
236 | }
237 |
238 | //
239 | // Called on keypress. Used to change node state when Space key is pressed
240 | // Invokes OnAfterCheck to do the real work
241 | //
242 | protected override void OnKeyDown(System.Windows.Forms.KeyEventArgs e)
243 | {
244 | base.OnKeyDown(e);
245 |
246 | // is the keypress a space? If not, discard it
247 | if (e.KeyCode == System.Windows.Forms.Keys.Space)
248 | {
249 | // toggle the node's checked status. This will then fire OnAfterCheck
250 | SelectedNode.Checked = !SelectedNode.Checked;
251 | }
252 | }
253 |
254 | //
255 | // Called when node is clicked by the mouse. Does nothing unless the image was clicked
256 | // Invokes OnAfterCheck to do the real work
257 | //
258 | protected override void OnNodeMouseClick(System.Windows.Forms.TreeNodeMouseClickEventArgs e)
259 | {
260 | base.OnNodeMouseClick(e);
261 |
262 | // is the click on the checkbox? If not, discard it
263 | System.Windows.Forms.TreeViewHitTestInfo info = HitTest(e.X, e.Y);
264 | if (info == null || info.Location != System.Windows.Forms.TreeViewHitTestLocations.StateImage)
265 | {
266 | return;
267 | }
268 |
269 | // toggle the node's checked status. This will then fire OnAfterCheck
270 | System.Windows.Forms.TreeNode tn = e.Node;
271 | tn.Checked = !tn.Checked;
272 | }
273 | }
274 | }
275 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 IWumboYouWumbo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flashpoint Component Tools
2 | This repository contains GUI utilities for downloading, managing, or removing a copy of the [Flashpoint Archive](https://flashpointarchive.org/) using the component system.
3 |
4 | A general-purpose command-line tool can be found [here](https://github.com/WumboSpasm/fpm).
5 |
6 | ## Tools
7 | - `FlashpointInstaller` - "Installs" (downloads) Flashpoint with any selected components to a specified folder, and optionally creates desktop and Start menu shortcuts.
8 | - `FlashpointManager` - Allows the user to add or remove components from an existing copy, or remove Flashpoint completely; intended for integration with the [Flashpoint Launcher](https://github.com/FlashpointProject/launcher).
--------------------------------------------------------------------------------