Beispiel #1
0
        public void SetData(ModDownload entry)
        {
            if (entry == null)
            {
                // Download details
                labelDownloadPublished.Text = null;
                labelSize.Text      = null;
                labelFileCount.Text = null;

                // Release details
                labelReleasePublished.Text = null;
                linkRelease.Text           = null;
                labelReleaseName.Text      = null;
                labelReleaseTag.Text       = null;
            }
            else
            {
                // Download details
                labelDownloadPublished.Text = entry.Updated.ToString(CultureInfo.CurrentCulture);
                labelSize.Text      = SizeSuffix.GetSizeSuffix(entry.Size);
                labelFileCount.Text = entry.FilesToDownload.ToString();

                // Release details
                labelReleasePublished.Text = entry.Published.ToString(CultureInfo.CurrentCulture);
                linkRelease.Text           = entry.ReleaseUrl;
                labelReleaseName.Text      = entry.Name;
                labelReleaseTag.Text       = entry.Version;
            }

            linkRelease.Enabled = !string.IsNullOrEmpty(linkRelease.Text);
            Enabled             = entry != null;
        }
Beispiel #2
0
        private void OnShown(object sender, EventArgs eventArgs)
        {
            DialogResult = DialogResult.OK;

            SetTaskCount(updates.Sum(update => update.FilesToDownload));

            using (var client = new UpdaterWebClient())
            {
                CancellationToken token = tokenSource.Token;

                void OnExtracting(object o, CancelEventArgs args)
                {
                    SetTaskAndStep("Extracting...");
                    args.Cancel = token.IsCancellationRequested;
                }

                void OnParsingManifest(object o, CancelEventArgs args)
                {
                    SetTaskAndStep("Parsing manifest...");
                    args.Cancel = token.IsCancellationRequested;
                }

                void OnApplyingManifest(object o, CancelEventArgs args)
                {
                    SetTaskAndStep("Applying manifest...");
                    args.Cancel = token.IsCancellationRequested;
                }

                void OnDownloadProgress(object o, DownloadProgressEventArgs args)
                {
                    SetProgress(args.BytesReceived / (double)args.TotalBytesToReceive);
                    SetTaskAndStep($"Downloading file {args.FileDownloading} of {args.FilesToDownload}:",
                                   $"({SizeSuffix.GetSizeSuffix(args.BytesReceived)} / {SizeSuffix.GetSizeSuffix(args.TotalBytesToReceive)})");
                    args.Cancel = token.IsCancellationRequested;
                }

                void OnDownloadCompleted(object o, CancelEventArgs args)
                {
                    NextTask();
                    args.Cancel = token.IsCancellationRequested;
                }

                int modIndex = 0;
                foreach (ModDownload update in updates)
                {
                    DialogResult result;

                    Title = $"Updating mod {++modIndex} of {updates.Count}: {update.Info.Name}";
                    SetTaskAndStep("Starting download...");

                    update.Extracting        += OnExtracting;
                    update.ParsingManifest   += OnParsingManifest;
                    update.ApplyingManifest  += OnApplyingManifest;
                    update.DownloadProgress  += OnDownloadProgress;
                    update.DownloadCompleted += OnDownloadCompleted;

                    do
                    {
                        result = DialogResult.Cancel;

                        try
                        {
                            // poor man's await Task.Run (not available in .net 4.0)
                            using (var task = new Task(() => update.Download(client, updatePath), token))
                            {
                                task.Start();

                                while (!task.IsCompleted && !task.IsCanceled)
                                {
                                    Application.DoEvents();
                                }

                                task.Wait(token);
                            }
                        }
                        catch (AggregateException ae)
                        {
                            ae.Handle(ex =>
                            {
                                result = MessageBox.Show(this, $"Failed to update mod { update.Info.Name }:\r\n{ ex.Message }"
                                                         + "\r\n\r\nPress Retry to try again, or Cancel to skip this mod.",
                                                         "Update Failed", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
                                return(true);
                            });
                        }
                    } while (result == DialogResult.Retry);

                    update.Extracting        -= OnExtracting;
                    update.ParsingManifest   -= OnParsingManifest;
                    update.ApplyingManifest  -= OnApplyingManifest;
                    update.DownloadProgress  -= OnDownloadProgress;
                    update.DownloadCompleted -= OnDownloadCompleted;
                }
            }
        }
        private void OnShown(object sender, EventArgs eventArgs)
        {
            DialogResult = DialogResult.OK;

            SetTaskCount(1);

            using (var client = new UpdaterWebClient())
            {
                CancellationToken token = tokenSource.Token;

                void OnDownloadProgress(object o, DownloadProgressEventArgs args)
                {
                    SetProgress(args.BytesReceived / (double)args.TotalBytesToReceive);
                    SetTaskAndStep($"Downloading file:",
                                   $"({SizeSuffix.GetSizeSuffix(args.BytesReceived)} / {SizeSuffix.GetSizeSuffix(args.TotalBytesToReceive)})");
                    args.Cancel = token.IsCancellationRequested;
                }

                void OnDownloadCompleted(object o, CancelEventArgs args)
                {
                    NextTask();
                    args.Cancel = token.IsCancellationRequested;
                }

                DialogResult result;

                SetTaskAndStep("Starting download...");

                do
                {
                    result = DialogResult.Cancel;

                    try
                    {
                        // poor man's await Task.Run (not available in .net 4.0)
                        using (var task = new Task(() =>
                        {
                            var cancelArgs = new CancelEventArgs(false);
                            DownloadProgressEventArgs downloadArgs = null;

                            void DownloadComplete(object _sender, AsyncCompletedEventArgs args)
                            {
                                lock (args.UserState)
                                {
                                    Monitor.Pulse(args.UserState);
                                }
                            }

                            void DownloadProgressChanged(object _sender, DownloadProgressChangedEventArgs args)
                            {
                                downloadArgs = new DownloadProgressEventArgs(args, 1, 1);
                                OnDownloadProgress(this, downloadArgs);
                                if (downloadArgs.Cancel)
                                {
                                    client.CancelAsync();                                     // This still doesn't work
                                }
                            }

                            var uri = new Uri(url);
                            string filePath = Path.Combine(updatePath, uri.Segments.Last());

                            var info = new FileInfo(filePath);
                            client.DownloadFileCompleted += DownloadComplete;
                            client.DownloadProgressChanged += DownloadProgressChanged;

                            var sync = new object();
                            lock (sync)
                            {
                                client.DownloadFileAsync(uri, filePath, sync);
                                Monitor.Wait(sync);
                            }

                            client.DownloadProgressChanged -= DownloadProgressChanged;
                            client.DownloadFileCompleted -= DownloadComplete;

                            if (cancelArgs.Cancel || downloadArgs?.Cancel == true)
                            {
                                return;
                            }

                            OnDownloadCompleted(this, cancelArgs);
                            if (cancelArgs.Cancel)
                            {
                                return;
                            }

                            string dataDir = Path.Combine(updatePath, Path.GetFileNameWithoutExtension(filePath));
                            if (!Directory.Exists(dataDir))
                            {
                                Directory.CreateDirectory(dataDir);
                            }

                            SetTaskAndStep("Extracting...");
                            if (token.IsCancellationRequested)
                            {
                                return;
                            }

                            Process.Start(new ProcessStartInfo("7z.exe", $"x -aoa -o\"{dataDir}\" \"{filePath}\"")
                            {
                                UseShellExecute = false, CreateNoWindow = true
                            }).WaitForExit();

                            Process.Start(Path.Combine(dataDir, "SAToolsHub.exe"), $"doupdate \"{dataDir}\" \"{Path.GetDirectoryName(Application.ExecutablePath)}\"");
                        }, token))
                        {
                            task.Start();

                            while (!task.IsCompleted && !task.IsCanceled)
                            {
                                Application.DoEvents();
                            }

                            task.Wait(token);
                        }
                    }
                    catch (AggregateException ae)
                    {
                        ae.Handle(ex =>
                        {
                            result = MessageBox.Show(this, $"Failed to update:\r\n{ ex.Message }",
                                                     "Update Failed", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
                            return(true);
                        });
                    }
                } while (result == DialogResult.Retry);
            }
        }
Beispiel #4
0
        /// <summary>
        /// Downloads files required for updating according to <see cref="Type"/>.
        /// </summary>
        /// <param name="client"><see cref="WebClient"/> to be used for downloading.</param>
        /// <param name="updatePath">Path to store downloaded files.</param>
        public void Download(WebClient client, string updatePath)
        {
            var cancelArgs = new CancelEventArgs(false);
            DownloadProgressEventArgs downloadArgs = null;

            int fileDownloading = 0;

            void downloadComplete(object sender, AsyncCompletedEventArgs args)
            {
                lock (args.UserState)
                {
                    Monitor.Pulse(args.UserState);
                }
            }

            void downloadProgressChanged(object sender, DownloadProgressChangedEventArgs args)
            {
                downloadArgs = new DownloadProgressEventArgs(args, fileDownloading, FilesToDownload);
                if (OnDownloadProgress(downloadArgs))
                {
                    ((WebClient)sender).CancelAsync();
                }
            }

            switch (Type)
            {
            case ModDownloadType.Archive:
            {
                var uri = new Uri(Url);
                if (!uri.Host.EndsWith("github.com", StringComparison.OrdinalIgnoreCase))
                {
                    var request = (HttpWebRequest)WebRequest.Create(uri);
                    request.Method = "HEAD";
                    var response = (HttpWebResponse)request.GetResponse();
                    uri = response.ResponseUri;
                    response.Close();
                }

                string filePath = Path.Combine(updatePath, uri.Segments.Last());

                var info = new FileInfo(filePath);
                if (info.Exists && info.Length == Size)
                {
                    if (OnDownloadCompleted(cancelArgs))
                    {
                        return;
                    }
                }
                else
                {
                    if (OnDownloadStarted(cancelArgs))
                    {
                        return;
                    }

                    client.DownloadFileCompleted   += downloadComplete;
                    client.DownloadProgressChanged += downloadProgressChanged;
                    ++fileDownloading;

                    var sync = new object();
                    lock (sync)
                    {
                        client.DownloadFileAsync(uri, filePath, sync);
                        Monitor.Wait(sync);
                    }

                    client.DownloadProgressChanged -= downloadProgressChanged;
                    client.DownloadFileCompleted   -= downloadComplete;

                    if (cancelArgs.Cancel || downloadArgs?.Cancel == true)
                    {
                        return;
                    }

                    if (OnDownloadCompleted(cancelArgs))
                    {
                        return;
                    }
                }

                string dataDir = Path.Combine(updatePath, Path.GetFileNameWithoutExtension(filePath));
                if (!Directory.Exists(dataDir))
                {
                    Directory.CreateDirectory(dataDir);
                }

                if (OnExtracting(cancelArgs))
                {
                    return;
                }

                Process process = Process.Start(
                    new ProcessStartInfo("7z.exe", $"x -aoa -o\"{dataDir}\" \"{filePath}\"")
                    {
                        UseShellExecute = false,
                        CreateNoWindow  = true
                    });

                if (process != null)
                {
                    process.WaitForExit();
                }
                else
                {
                    throw new NullReferenceException("Failed to create 7z process");
                }

                string workDir = Path.GetDirectoryName(ModInfo.GetModFiles(new DirectoryInfo(dataDir)).FirstOrDefault());

                if (string.IsNullOrEmpty(workDir))
                {
                    throw new DirectoryNotFoundException($"Unable to locate mod.ini in \"{dataDir}\"");
                }

                string newManPath = Path.Combine(workDir, "mod.manifest");
                string oldManPath = Path.Combine(Folder, "mod.manifest");

                if (OnParsingManifest(cancelArgs))
                {
                    return;
                }

                if (!File.Exists(newManPath) || !File.Exists(oldManPath))
                {
                    CopyDirectory(new DirectoryInfo(workDir), Directory.CreateDirectory(Folder));
                    Directory.Delete(dataDir, true);

                    if (File.Exists(filePath))
                    {
                        File.Delete(filePath);
                    }

                    return;
                }

                if (OnParsingManifest(cancelArgs))
                {
                    return;
                }

                List <ModManifestEntry> newManifest = ModManifest.FromFile(newManPath);

                if (OnApplyingManifest(cancelArgs))
                {
                    return;
                }

                List <ModManifestEntry> oldManifest = ModManifest.FromFile(oldManPath);
                List <string>           oldFiles    = oldManifest.Except(newManifest)
                                                      .Select(x => Path.Combine(Folder, x.FilePath))
                                                      .ToList();

                foreach (string file in oldFiles)
                {
                    if (File.Exists(file))
                    {
                        File.Delete(file);
                    }
                }

                RemoveEmptyDirectories(oldManifest, newManifest);

                foreach (ModManifestEntry file in newManifest)
                {
                    string dir = Path.GetDirectoryName(file.FilePath);
                    if (!string.IsNullOrEmpty(dir))
                    {
                        string newDir = Path.Combine(Folder, dir);
                        if (!Directory.Exists(newDir))
                        {
                            Directory.CreateDirectory(newDir);
                        }
                    }

                    var sourceFile = new FileInfo(Path.Combine(workDir, file.FilePath));
                    var destFile   = new FileInfo(Path.Combine(Folder, file.FilePath));

                    if (destFile.Exists)
                    {
                        destFile.Delete();
                    }

                    sourceFile.Attributes &= ~FileAttributes.ReadOnly;
                    sourceFile.MoveTo(destFile.FullName);
                }

                File.Copy(newManPath, oldManPath, true);

                void removeReadOnly(DirectoryInfo dir)
                {
                    foreach (DirectoryInfo d in dir.GetDirectories())
                    {
                        removeReadOnly(d);
                        d.Attributes &= ~FileAttributes.ReadOnly;
                    }
                }

                removeReadOnly(new DirectoryInfo(dataDir));

                Directory.Delete(dataDir, true);
                File.WriteAllText(Path.Combine(Folder, "mod.version"), Updated.ToString(DateTimeFormatInfo.InvariantInfo));

                if (File.Exists(filePath))
                {
                    File.Delete(filePath);
                }

                break;
            }

            case ModDownloadType.Modular:
            {
                List <ModManifestDiff> newEntries = ChangedFiles
                                                    .Where(x => x.State == ModManifestState.Added || x.State == ModManifestState.Changed)
                                                    .ToList();

                var    uri     = new Uri(Url);
                string tempDir = Path.Combine(updatePath, uri.Segments.Last());

                if (!Directory.Exists(tempDir))
                {
                    Directory.CreateDirectory(tempDir);
                }

                var sync = new object();

                foreach (ModManifestDiff i in newEntries)
                {
                    string filePath = Path.Combine(tempDir, i.Current.FilePath);
                    string dir      = Path.GetDirectoryName(filePath);

                    if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
                    {
                        Directory.CreateDirectory(dir);
                    }

                    if (OnDownloadStarted(cancelArgs))
                    {
                        return;
                    }

                    var info = new FileInfo(filePath);
                    ++fileDownloading;

                    if (!info.Exists || info.Length != i.Current.FileSize ||
                        !i.Current.Checksum.Equals(ModManifestGenerator.GetFileHash(filePath), StringComparison.OrdinalIgnoreCase))
                    {
                        client.DownloadFileCompleted   += downloadComplete;
                        client.DownloadProgressChanged += downloadProgressChanged;

                        lock (sync)
                        {
                            client.DownloadFileAsync(new Uri(uri, i.Current.FilePath), filePath, sync);
                            Monitor.Wait(sync);
                        }

                        client.DownloadProgressChanged -= downloadProgressChanged;
                        client.DownloadFileCompleted   -= downloadComplete;

                        info.Refresh();

                        if (info.Length != i.Current.FileSize)
                        {
                            throw new Exception(string.Format("Size of downloaded file \"{0}\" ({1}) differs from manifest ({2}).",
                                                              i.Current.FilePath, SizeSuffix.GetSizeSuffix(info.Length), SizeSuffix.GetSizeSuffix(i.Current.FileSize)));
                        }

                        string hash = ModManifestGenerator.GetFileHash(filePath);
                        if (!i.Current.Checksum.Equals(hash, StringComparison.OrdinalIgnoreCase))
                        {
                            throw new Exception(string.Format("Checksum of downloaded file \"{0}\" ({1}) differs from manifest ({2}).",
                                                              i.Current.FilePath, hash, i.Current.Checksum));
                        }
                    }

                    if (cancelArgs.Cancel || downloadArgs?.Cancel == true)
                    {
                        return;
                    }

                    if (OnDownloadCompleted(cancelArgs))
                    {
                        return;
                    }
                }

                client.DownloadFileCompleted += downloadComplete;
                lock (sync)
                {
                    client.DownloadFileAsync(new Uri(uri, "mod.manifest"), Path.Combine(tempDir, "mod.manifest"), sync);
                    Monitor.Wait(sync);
                }

                client.DownloadFileCompleted -= downloadComplete;

                // Handle all non-removal file operations (move, rename)
                List <ModManifestDiff> movedEntries = ChangedFiles.Except(newEntries)
                                                      .Where(x => x.State == ModManifestState.Moved)
                                                      .ToList();

                if (OnApplyingManifest(cancelArgs))
                {
                    return;
                }

                // Handle existing entries marked as moved.
                foreach (ModManifestDiff i in movedEntries)
                {
                    ModManifestEntry old = i.Last;

                    // This would be considered an error...
                    if (old == null)
                    {
                        continue;
                    }

                    string oldPath = Path.Combine(Folder, old.FilePath);
                    string newPath = Path.Combine(tempDir, i.Current.FilePath);

                    string dir = Path.GetDirectoryName(newPath);

                    if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
                    {
                        Directory.CreateDirectory(dir);
                    }

                    File.Copy(oldPath, newPath, true);
                }

                // Now move the stuff from the temporary folder over to the working directory.
                foreach (ModManifestDiff i in newEntries.Concat(movedEntries))
                {
                    string tempPath = Path.Combine(tempDir, i.Current.FilePath);
                    string workPath = Path.Combine(Folder, i.Current.FilePath);
                    string dir      = Path.GetDirectoryName(workPath);

                    if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
                    {
                        Directory.CreateDirectory(dir);
                    }

                    File.Copy(tempPath, workPath, true);
                }

                // Once that has succeeded we can safely delete files that have been marked for removal.
                List <ModManifestDiff> removedEntries = ChangedFiles
                                                        .Where(x => x.State == ModManifestState.Removed)
                                                        .ToList();

                foreach (string path in removedEntries.Select(i => Path.Combine(Folder, i.Current.FilePath)).Where(File.Exists))
                {
                    File.Delete(path);
                }

                // Same for files that have been moved.
                foreach (string path in movedEntries
                         .Where(x => newEntries.All(y => y.Current.FilePath != x.Last.FilePath))
                         .Select(i => Path.Combine(Folder, i.Last.FilePath)).Where(File.Exists))
                {
                    File.Delete(path);
                }

                string oldManPath = Path.Combine(Folder, "mod.manifest");
                string newManPath = Path.Combine(tempDir, "mod.manifest");

                if (File.Exists(oldManPath))
                {
                    List <ModManifestEntry> oldManifest = ModManifest.FromFile(oldManPath);
                    List <ModManifestEntry> newManifest = ModManifest.FromFile(newManPath);

                    // Remove directories that are now empty.
                    RemoveEmptyDirectories(oldManifest, newManifest);
                }

                // And last but not least, copy over the new manifest.
                File.Copy(newManPath, oldManPath, true);
                break;
            }

            default:
                throw new ArgumentOutOfRangeException();
            }
        }