├── .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). --------------------------------------------------------------------------------