public void CheckForUpdates(HashWebClient.RemoteFileInfo patches)
        {
            if (_isChecking)
                return;

            _lastPatchesJsonLoc = patches;
            _isChecking = true;

            Status = DayZeroLauncherUpdater.STATUS_CHECKINGFORUPDATES;
            new Thread(() =>
            {
                try
                {
                    string patchesFileName = PatchesMeta.GetFileName();
                    PatchesMeta patchInfo = null;

                    HashWebClient.DownloadWithStatusDots(patches, patchesFileName, DayZeroLauncherUpdater.STATUS_CHECKINGFORUPDATES,
                        newStatus => { Status = newStatus; },
                        (wc, fileInfo, destPath) => { patchInfo = PatchesMeta.LoadFromFile(patchesFileName); });

                    if (patchInfo != null)
                    {
                        Status = DayZeroLauncherUpdater.STATUS_CHECKINGFORUPDATES;
                        Thread.Sleep(100);

                        try
                        {
                            PatchesMeta.PatchInfo thePatch = patchInfo.Updates.Where(x => x.Version == patchInfo.LatestRevision).Single();
                            SetLatestServerVersion(thePatch);

                            Status = VersionMismatch ? (DayZeroLauncherUpdater.STATUS_OUTOFDATE) : (DayZeroLauncherUpdater.STATUS_UPTODATE);
                        }
                        catch (Exception)
                        {
                            Status = "Could not determine revision";
                        }
                    }
                }
                finally
                {
                    _isChecking = false;
                }
            }).Start();
        }
        public void CheckForUpdates(HashWebClient.RemoteFileInfo mods)
        {
            if (_isChecking)
                return;

            _lastModsJsonLoc = mods;
            _isChecking = true;

            new Thread(() =>
            {
                try
                {
                    string modsFileName = ModsMeta.GetFileName();
                    ModsMeta modsInfo = null;

                    HashWebClient.DownloadWithStatusDots(mods, modsFileName, DayZeroLauncherUpdater.STATUS_CHECKINGFORUPDATES,
                        newStatus => { Status = newStatus; },
                        (wc, fileInfo, destPath) => { modsInfo = ModsMeta.LoadFromFile(modsFileName); });

                    if (modsInfo != null)
                    {
                        Status = DayZeroLauncherUpdater.STATUS_CHECKINGFORUPDATES;
                        Thread.Sleep(100);

                        try
                        {
                            ModsMeta.ModInfo theMod =
                                modsInfo.Mods.Where(x => x.Version.Equals(modsInfo.LatestModVersion, StringComparison.OrdinalIgnoreCase))
                                    .Single();
                            SetLatestModVersion(theMod);

                            string currVersion = CalculatedGameSettings.Current.ModContentVersion;
                            if (!theMod.Version.Equals(currVersion, StringComparison.OrdinalIgnoreCase))
                            {
                                Status = DayZeroLauncherUpdater.STATUS_OUTOFDATE;

                                //this lets them seed/repair version they already have if it's not discontinued
                                ModsMeta.ModInfo currMod =
                                    modsInfo.Mods.SingleOrDefault(x => x.Version.Equals(currVersion, StringComparison.OrdinalIgnoreCase));
                                if (currMod != null)
                                    DownloadSpecificVersion(currMod, false);
                                else //try getting it from file cache (necessary for switching branches)
                                    DownloadLocalVersion(currVersion, false);
                            }
                            else
                            {
                                Status = DayZeroLauncherUpdater.STATUS_UPTODATE;
                                DownloadLatestVersion(false);
                            }
                        }
                        catch (Exception)
                        {
                            Status = "Could not determine revision";
                        }
                    }
                }
                finally
                {
                    _isChecking = false;
                }
            }).Start();
        }
		public static void DownloadWithStatusDots(RemoteFileInfo fileInfo, string destPath, string initialStatus,
			StatusChangeString statusCb, StatusDownloadComplete finishCb)
		{
			using (var downloadingEvt = new ManualResetEvent(false))
			{
				string updateInfoString = initialStatus.Replace("...", String.Empty);
				statusCb(updateInfoString);

				var wc = new HashWebClient();
				wc.DownloadFileCompleted += (source, evt) =>
				{
					if (evt.Cancelled)
						statusCb("Async operation cancelled");
					else if (evt.Error != null)
						statusCb(evt.Error.Message);
					else
					{
						try
						{
							if (finishCb != null)
								finishCb(wc, fileInfo, destPath);
						}
						catch (Exception ex)
						{
							statusCb(ex.Message);
						}
					}
					downloadingEvt.Set();
				};
				wc.BeginDownload(fileInfo, destPath);

				int numDots = 0;
				uint loops = 0;
				while (downloadingEvt.WaitOne(10) == false)
				{
					if (loops >= 10)
					{
						numDots = (numDots + 1)%3;
						statusCb(updateInfoString + new String('.', numDots));
						loops = 0;
					}

					loops++;
				}
				if (wc != null)
				{
					wc.Dispose();
					wc = null;
				}
			}
		}
        public TorrentUpdater(string versionString, List<MetaAddon> addOns, bool fullSystemCheck, TorrentLauncher downloader,
			DayZUpdater updater, bool errorMsgsOnly)
        {
            addOnTorrents = new List<AddOnTorrent>();
            this.versionString = versionString;
            this.fullSystemCheck = fullSystemCheck;
            this.downloader = downloader;
            this.updater = updater;
            this.errorMsgsOnly = errorMsgsOnly;

            string torrentsDir = null;
            try
            {
                torrentsDir = Path.Combine(UserSettings.ContentMetaPath, versionString);
                {
                    var dirInfo = new DirectoryInfo(torrentsDir);
                    if (!dirInfo.Exists)
                        dirInfo = Directory.CreateDirectory(torrentsDir);
                }
            }
            catch (Exception ex)
            {
                updater.Status = "Error creating torrents directory";
                downloader.Status = ex.Message;
                downloader.IsRunning = false;

                return;
            }

            foreach (MetaAddon addOn in addOns)
            {
                var newAddOn = new AddOnTorrent();
                newAddOn.Meta = addOn;
                newAddOn.torrentFileName = Path.Combine(torrentsDir,
                    newAddOn.Meta.Description + "-" + newAddOn.Meta.Version + ".torrent");
                newAddOn.torrentSavePath = null; //will be filled in if successfull download
                addOnTorrents.Add(newAddOn);
            }

            //delete .torrent files that do not match the ones we want
            string[] allTorrents = Directory.GetFiles(torrentsDir, "*.torrent", SearchOption.TopDirectoryOnly);
            foreach (string torrentPath in allTorrents)
            {
                if (
                    addOnTorrents.Count(
                        naot => { return naot.torrentFileName.Equals(torrentPath, StringComparison.InvariantCultureIgnoreCase); }) < 1)
                {
                    //this is an unwanted torrent file
                    try
                    {
                        var fileInfo = new FileInfo(torrentPath);
                        if (fileInfo.IsReadOnly)
                        {
                            fileInfo.IsReadOnly = false;
                            fileInfo.Refresh();
                        }
                        fileInfo.Delete();
                    }
                    catch (Exception)
                    {
                    }
                }
            }

            for (int i = 0; i < addOnTorrents.Count; i++)
            {
                int idxCopy = i;
                AddOnTorrent newAddOn = addOnTorrents[i];
                try
                {
                    var wc = new HashWebClient();
                    wc.DownloadFileCompleted += (sender, args) => { TorrentDownloadComplete(sender, args, idxCopy); };
                    wc.BeginDownload(newAddOn.Meta.Torrent, newAddOn.torrentFileName);
                }
                catch (Exception ex)
                {
                    updater.Status = "Error starting torrent file download";
                    downloader.Status = ex.Message;
                    downloader.IsRunning = false;
                    return;
                }
            }
        }
        private void StartInstallerDownload(int arrIdx)
        {
            InstallersMeta.InstallerInfo inst = installers.ElementAt(arrIdx);

            LowerProgressText = "Downloading " + inst.Archive.Url;
            LowerProgressValue = 0;
            LowerProgressLimit = 100;

            var wc = new HashWebClient();
            wc.DownloadProgressChanged += (sender, args) => { LowerProgressValue = args.ProgressPercentage; };
            wc.DownloadFileCompleted += (sender, args) =>
            {
                if (HandlePossibleError("Error " + UpperProgressText, args))
                    return;

                LowerProgressLimit = 100;
                LowerProgressValue = 100;

                UpperProgressValue = UpperProgressValue + 1;
                if (UpperProgressValue == UpperProgressLimit) //downloaded all installers
                    VerifyAndInstallPackages();
                else
                    StartInstallerDownload(arrIdx + 1);
            };
            wc.BeginDownload(inst.Archive, inst.GetArchiveFileName());
        }
        protected void GetInstallersMeta()
        {
            Closeable = false;
            UpperProgressValue = 0;
            UpperProgressLimit = 1;

            LocatorInfo locator = CalculatedGameSettings.Current.Locator;
            if (locator == null)
            {
                UpperProgressText = "Please check for updates first.";
                UpperProgressLimit = 0;
                Closeable = true;
                return;
            }

            UpperProgressText = "Getting installer info...";
            string installersFileName = InstallersMeta.GetFileName();
            var wc = new HashWebClient();
            wc.DownloadProgressChanged += (sender, args) =>
            {
                UpperProgressLimit = 100;
                UpperProgressValue = args.ProgressPercentage;
            };
            wc.DownloadFileCompleted += (sender, args) =>
            {
                if (HandlePossibleError("Installer index download failed", args))
                    return;

                UpperProgressLimit = 100;
                UpperProgressValue = 100;

                UpperProgressText = "Parsing installer info...";
                InstallersMeta newInsts = null;
                try
                {
                    newInsts = InstallersMeta.LoadFromFile(installersFileName);
                }
                catch (Exception ex)
                {
                    HandleException("Error parsing installer info", ex);
                    return;
                }

                var wntInsts = new List<InstallersMeta.InstallerInfo>();
                try
                {
                    foreach (MetaAddon addon in addOns)
                    {
                        InstallersMeta.InstallerInfo instMatch =
                            wntInsts.FirstOrDefault(x => { return String.Equals(x.Version, addon.InstallerName); });
                        if (instMatch == null)
                            instMatch = newInsts.Installers.FirstOrDefault(x => { return String.Equals(x.Version, addon.InstallerName); });

                        if (instMatch == null)
                            throw new InstallerNotFound(addon.InstallerName);
                        wntInsts.Add(instMatch);
                    }
                }
                catch (Exception ex)
                {
                    HandleException("Error matching installers", ex);
                    return;
                }

                installers = wntInsts;
                DownloadInstallerPackages();
            };
            wc.BeginDownload(locator.Installers, installersFileName);
        }
        public void DownloadAndInstall(int revision, HashWebClient.RemoteFileInfo archiveInfo, bool steamBeta,
			string steamBuild, UpdatesView view)
        {
            if (steamBeta)
            {
                const int appId = 33930;
                string gameName = "Arma 2: Operation Arrowhead Beta";
                DirectoryInfo armaPath = null;

                try
                {
                    armaPath = new DirectoryInfo(CalculatedGameSettings.Current.Arma2OAPath);
                }
                catch (ArgumentException aex)
                {
                    bool overridenPath = string.IsNullOrWhiteSpace(UserSettings.Current.GameOptions.Arma2OADirectoryOverride);

                    Execute.OnUiThreadSync(() =>
                    {
                        var popup = new InfoPopup("Invalid path", MainWindow.GetWindow(view));
                        popup.Headline.Content = "Game could not be found";
                        popup.SetMessage(overridenPath
                            ? "Invalid game override path, please enter a new game path or remove it"
                            : "Game could not located via the registry, please enter an override path");

                        popup.Show();
                    }, null, DispatcherPriority.Input);

                    return;
                }

                for (armaPath = armaPath.Parent; armaPath != null; armaPath = armaPath.Parent)
                {
                    if (armaPath.Name.Equals("steamapps", StringComparison.OrdinalIgnoreCase))
                    {
                        string manifestName = "appmanifest_" + appId.ToString() + ".acf";
                        string fullManifestPath = Path.Combine(armaPath.FullName, manifestName);
                        if (File.Exists(fullManifestPath))
                        {
                            // Kill Steam so we can edit the game configuration.
                            Process[] processes = Process.GetProcessesByName("Steam");

                            foreach (Process process in processes)
                            {
                                // #YOLO
                                try
                                {
                                    process.Kill();
                                    process.WaitForExit();
                                }
                                catch
                                {
                                    MessageBox.Show("Unable to shut down steam to start patching.",
                                        "Patch error", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                                    return;
                                }
                            }

                            var acfKeys = new KeyValue();
                            var reader = new StreamReader(fullManifestPath);
                            var acfReader = new KVTextReader(acfKeys, reader.BaseStream);
                            reader.Close();
                            KeyValue currentBuild = acfKeys.Children.FirstOrDefault(k => k.Name == "buildid");
                            if (!String.IsNullOrEmpty(currentBuild.Value))
                            {
                                if (Equals(currentBuild.Value, steamBuild))
                                {
                                    Execute.OnUiThreadSync(() =>
                                    {
                                        var popup = new InfoPopup("User intervention required", MainWindow.GetWindow(view));
                                        popup.Headline.Content = "Game update using Steam";
                                        popup.SetMessage(gameName + " might be corrupted.\n" +
                                                         "Please validate your client files manually.\n" +
                                                         "Or by clicking on the following link:");
                                        popup.SetLink("steam://validate/" + appId.ToString() + "/", "Update " + gameName);
                                        popup.Closed += (sender, args) => view.CheckForUpdates();
                                        popup.Show();
                                    }, null, DispatcherPriority.Input);
                                }
                                else
                                {
                                    KeyValue gameState = acfKeys.Children.FirstOrDefault(k => k.Name == "StateFlags");
                                    if (!String.IsNullOrEmpty(gameState.Value))
                                    {
                                        currentBuild.Value = steamBuild;
                                        gameState.Value = "2";
                                        acfKeys.SaveToFile(fullManifestPath, false);

                                        Thread.Sleep(1000);

                                        Execute.OnUiThreadSync(() =>
                                        {
                                            var popup = new InfoPopup("User intervention required", MainWindow.GetWindow(view));
                                            popup.Headline.Content = "Game update using Steam";
                                            popup.SetMessage(gameName + " branch switched to BETA.\n" +
                                                             "Please restart Steam to download update.");
                                            popup.Closed += (sender, args) => view.CheckForUpdates();
                                            popup.Show();
                                        }, null, DispatcherPriority.Input);
                                    }
                                }
                            }
                            else
                            {
                                MessageBox.Show("Patching failed, '" + gameName + "' is not located inside a SteamLibrary folder.",
                                    "Patch error", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                                return;
                            }

                            return;
                        }
                        else
                        {
                            Execute.OnUiThreadSync(() =>
                            {
                                var popup = new InfoPopup("User intervention required", MainWindow.GetWindow(view));
                                popup.Headline.Content = "Game update using Steam";
                                popup.SetMessage(gameName + " is not installed.\n" +
                                                 "Please install it from the Library tab.\n" +
                                                 "Or by clicking on the following link:");
                                popup.SetLink("steam://install/" + appId.ToString() + "/", "Install " + gameName);
                                popup.Closed += (sender, args) => view.CheckForUpdates();
                                popup.Show();
                            }, null, DispatcherPriority.Input);

                            return;
                        }
                    }
                }
                if (armaPath == null)
                {
                    MessageBox.Show("Patching failed, '" + gameName + "' is not located inside a SteamLibrary folder.",
                        "Patch error", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                    return;
                }
            }
            else
            {
                DownloadAndInstall(revision, archiveInfo);
            }
        }
        public void DownloadAndInstall(int revision, HashWebClient.RemoteFileInfo archiveInfo)
        {
            IsRunning = true;
            Status = "Getting file info...";

            string extractedFolderLocation = Path.Combine(UserSettings.PatchesPath, revision.ToString());
            string zipFileLocation = extractedFolderLocation + ".zip";

            var wc = new HashWebClient();
            wc.DownloadProgressChanged +=
                (sender, args) => { Status = string.Format("Downloading... {0}%", args.ProgressPercentage); };
            wc.DownloadFileCompleted += (sender, args) =>
            {
                if (args.Error != null)
                {
                    Status = "Error: " + args.Error.Message;
                    IsRunning = false;
                    return;
                }
                ExtractFile(zipFileLocation, extractedFolderLocation);
            };
            wc.BeginDownload(archiveInfo, zipFileLocation);
        }
        public void StartFromNetContent(string versionString, bool forceFullSystemsCheck,
			HashWebClient.RemoteFileInfo jsonIndex, DayZUpdater updater, bool errorMsgsOnly = false)
        {
            if (jsonIndex == null)
            {
                MessageBox.Show("No version index specified, please Check first.", "Error initiating torrent download",
                    MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (string.IsNullOrWhiteSpace(versionString))
            {
                MessageBox.Show("Invalid version specified for download", "Error initiating torrent download", MessageBoxButton.OK,
                    MessageBoxImage.Error);
                return;
            }

            if (IsRunning)
            {
                if (_updatingVersion.Equals(versionString, StringComparison.OrdinalIgnoreCase))
                    return; //already running for this same version
            }

            _updatingVersion = versionString;
            if (!errorMsgsOnly)
                updater.Status = DayZeroLauncherUpdater.STATUS_DOWNLOADING;

            Status = "Initiating Download...";

            string metaJsonFilename = MetaModDetails.GetFileName(versionString);
            var wc = new HashWebClient();
            wc.DownloadFileCompleted += (sender, args) =>
            {
                if (args.Cancelled)
                {
                    Status = updater.Status = "Async operation cancelled";
                    IsRunning = false;
                    _gameLauncher.SetModDetails(null, true, null);
                }
                else if (args.Error != null)
                {
                    updater.Status = "Error downloading content index file";
                    Status = args.Error.Message;
                    IsRunning = false;
                    _gameLauncher.SetModDetails(null, false, args.Error);
                }
                else
                    ContinueFromContentFile(versionString, metaJsonFilename, forceFullSystemsCheck, updater, errorMsgsOnly);
            };
            wc.BeginDownload(jsonIndex, metaJsonFilename);
        }