private async Task <SolutionStateChecksums> ComputeChecksumsAsync(CancellationToken cancellationToken)
        {
            try
            {
                using (Logger.LogBlock(FunctionId.SolutionState_ComputeChecksumsAsync, FilePath, cancellationToken))
                {
                    // get states by id order to have deterministic checksum
                    var orderedProjectIds    = ChecksumCache.GetOrCreate(ProjectIds, _ => ProjectIds.OrderBy(id => id.Id).ToImmutableArray());
                    var projectChecksumTasks = orderedProjectIds.Select(id => ProjectStates[id])
                                               .Where(s => RemoteSupportedLanguages.IsSupported(s.Language))
                                               .Select(s => s.GetChecksumAsync(cancellationToken));

                    var serializer      = _solutionServices.Workspace.Services.GetService <ISerializerService>();
                    var infoChecksum    = serializer.CreateChecksum(SolutionAttributes, cancellationToken);
                    var optionsChecksum = serializer.CreateChecksum(Options, cancellationToken);

                    var analyzerReferenceChecksums = ChecksumCache.GetOrCreate <AnalyzerReferenceChecksumCollection>(AnalyzerReferences,
                                                                                                                     _ => new AnalyzerReferenceChecksumCollection(AnalyzerReferences.Select(r => serializer.CreateChecksum(r, cancellationToken)).ToArray()));

                    var projectChecksums = await Task.WhenAll(projectChecksumTasks).ConfigureAwait(false);

                    return(new SolutionStateChecksums(infoChecksum, optionsChecksum, new ProjectChecksumCollection(projectChecksums), analyzerReferenceChecksums));
                }
            }
            catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
示例#2
0
        private async Task <ProjectStateChecksums> ComputeChecksumsAsync(CancellationToken cancellationToken)
        {
            using (Logger.LogBlock(FunctionId.ProjectState_ComputeChecksumsAsync, FilePath, cancellationToken))
            {
                // get states by id order to have deterministic checksum
                var documentChecksumsTasks          = DocumentIds.Select(id => DocumentStates[id].GetChecksumAsync(cancellationToken));
                var additionalDocumentChecksumTasks = AdditionalDocumentIds.Select(id => AdditionalDocumentStates[id].GetChecksumAsync(cancellationToken));

                var serializer = new Serializer(_solutionServices.Workspace);

                var infoChecksum = serializer.CreateChecksum(ProjectInfo.Attributes, cancellationToken);

                // these compiler objects doesn't have good place to cache checksum. but rarely ever get changed.
                var compilationOptionsChecksum = SupportsCompilation ? ChecksumCache.GetOrCreate(CompilationOptions, _ => serializer.CreateChecksum(CompilationOptions, cancellationToken)) : Checksum.Null;
                var parseOptionsChecksum       = SupportsCompilation ? ChecksumCache.GetOrCreate(ParseOptions, _ => serializer.CreateChecksum(ParseOptions, cancellationToken)) : Checksum.Null;

                var projectReferenceChecksums  = ChecksumCache.GetOrCreate <ProjectReferenceChecksumCollection>(ProjectReferences, _ => new ProjectReferenceChecksumCollection(ProjectReferences.Select(r => serializer.CreateChecksum(r, cancellationToken)).ToArray()));
                var metadataReferenceChecksums = ChecksumCache.GetOrCreate <MetadataReferenceChecksumCollection>(MetadataReferences, _ => new MetadataReferenceChecksumCollection(MetadataReferences.Select(r => serializer.CreateChecksum(r, cancellationToken)).ToArray()));
                var analyzerReferenceChecksums = ChecksumCache.GetOrCreate <AnalyzerReferenceChecksumCollection>(AnalyzerReferences, _ => new AnalyzerReferenceChecksumCollection(AnalyzerReferences.Select(r => serializer.CreateChecksum(r, cancellationToken)).ToArray()));

                var documentChecksums = await Task.WhenAll(documentChecksumsTasks).ConfigureAwait(false);

                var additionalChecksums = await Task.WhenAll(additionalDocumentChecksumTasks).ConfigureAwait(false);

                return(new ProjectStateChecksums(
                           infoChecksum,
                           compilationOptionsChecksum,
                           parseOptionsChecksum,
                           new DocumentChecksumCollection(documentChecksums),
                           projectReferenceChecksums,
                           metadataReferenceChecksums,
                           analyzerReferenceChecksums,
                           new TextDocumentChecksumCollection(additionalChecksums)));
            }
        }
示例#3
0
 private Checksum GetParseOptionsChecksum(ISerializerService serializer) =>
 this.SupportsCompilation
         ? ChecksumCache.GetOrCreate(
     this.ParseOptions,
     _ => serializer.CreateParseOptionsChecksum(this.ParseOptions)
     )
         : Checksum.Null;
示例#4
0
 private static Checksum GetMetadataChecksumSlow(Solution solution, PortableExecutableReference reference, CancellationToken cancellationToken)
 {
     return(ChecksumCache.GetOrCreate(reference, _ =>
     {
         var serializer = solution.Workspace.Services.GetService <ISerializerService>();
         var checksum = serializer.CreateChecksum(reference, cancellationToken);
         return checksum;
     }));
 }
示例#5
0
 public static Checksum GetMetadataChecksum(
     Solution solution, PortableExecutableReference reference, CancellationToken cancellationToken)
 {
     // We can reuse the index for any given reference as long as it hasn't changed.
     // So our checksum is just the checksum for the PEReference itself.
     return(ChecksumCache.GetOrCreate(reference, _ =>
     {
         var serializer = solution.Workspace.Services.GetService <ISerializerService>();
         var checksum = serializer.CreateChecksum(reference, cancellationToken);
         return checksum;
     }));
 }
示例#6
0
        private static Checksum GetMetadataChecksumSlow(Solution solution, PortableExecutableReference reference, CancellationToken cancellationToken)
        {
            return(ChecksumCache.GetOrCreate(reference, _ =>
            {
                var serializer = solution.Workspace.Services.GetService <ISerializerService>();
                var checksum = serializer.CreateChecksum(reference, cancellationToken);

                // Include serialization format version in our checksum.  That way if the
                // version ever changes, all persisted data won't match the current checksum
                // we expect, and we'll recompute things.
                return Checksum.Create(checksum, SerializationFormatChecksum);
            }));
        }
示例#7
0
        public static Checksum GetMetadataChecksum(
            Solution solution, PortableExecutableReference reference, CancellationToken cancellationToken)
        {
            // We can reuse the index for any given reference as long as it hasn't changed.
            // So our checksum is just the checksum for the PEReference itself.
            // First see if the value is already in the cache, to avoid an allocation if possible.
            if (ChecksumCache.TryGetValue(reference, out var cached))
            {
                return(cached);
            }

            // Break things up to the fast path above and this slow path where we allocate a closure.
            return(GetMetadataChecksumSlow(solution, reference, cancellationToken));
        }
        private async Task <ProjectStateChecksums> ComputeChecksumsAsync(CancellationToken cancellationToken)
        {
            try
            {
                using (Logger.LogBlock(FunctionId.ProjectState_ComputeChecksumsAsync, FilePath, cancellationToken))
                {
                    // Here, we use the _documentStates and _additionalDocumentStates and visit them in order; we ensure that those are
                    // sorted by ID so we have a consistent sort.
                    var documentChecksumsTasks              = _documentStates.Select(pair => pair.Value.GetChecksumAsync(cancellationToken));
                    var additionalDocumentChecksumTasks     = _additionalDocumentStates.Select(pair => pair.Value.GetChecksumAsync(cancellationToken));
                    var analyzerConfigDocumentChecksumTasks = _analyzerConfigDocumentStates.Select(pair => pair.Value.GetChecksumAsync(cancellationToken));

                    var serializer = _solutionServices.Workspace.Services.GetService <ISerializerService>();

                    var infoChecksum = serializer.CreateChecksum(ProjectInfo.Attributes, cancellationToken);

                    // these compiler objects doesn't have good place to cache checksum. but rarely ever get changed.
                    var compilationOptionsChecksum = SupportsCompilation ? ChecksumCache.GetOrCreate(CompilationOptions, _ => serializer.CreateChecksum(CompilationOptions, cancellationToken)) : Checksum.Null;
                    cancellationToken.ThrowIfCancellationRequested();
                    var parseOptionsChecksum = GetParseOptionsChecksum(serializer);

                    var projectReferenceChecksums  = ChecksumCache.GetOrCreate <ProjectReferenceChecksumCollection>(ProjectReferences, _ => new ProjectReferenceChecksumCollection(ProjectReferences.Select(r => serializer.CreateChecksum(r, cancellationToken)).ToArray()));
                    var metadataReferenceChecksums = ChecksumCache.GetOrCreate <MetadataReferenceChecksumCollection>(MetadataReferences, _ => new MetadataReferenceChecksumCollection(MetadataReferences.Select(r => serializer.CreateChecksum(r, cancellationToken)).ToArray()));
                    var analyzerReferenceChecksums = ChecksumCache.GetOrCreate <AnalyzerReferenceChecksumCollection>(AnalyzerReferences, _ => new AnalyzerReferenceChecksumCollection(AnalyzerReferences.Select(r => serializer.CreateChecksum(r, cancellationToken)).ToArray()));

                    var documentChecksums = await Task.WhenAll(documentChecksumsTasks).ConfigureAwait(false);

                    var additionalChecksums = await Task.WhenAll(additionalDocumentChecksumTasks).ConfigureAwait(false);

                    var analyzerConfigDocumentChecksums = await Task.WhenAll(analyzerConfigDocumentChecksumTasks).ConfigureAwait(false);

                    return(new ProjectStateChecksums(
                               infoChecksum,
                               compilationOptionsChecksum,
                               parseOptionsChecksum,
                               new DocumentChecksumCollection(documentChecksums),
                               projectReferenceChecksums,
                               metadataReferenceChecksums,
                               analyzerReferenceChecksums,
                               new TextDocumentChecksumCollection(additionalChecksums),
                               new AnalyzerConfigDocumentChecksumCollection(analyzerConfigDocumentChecksums)));
                }
            }
            catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
示例#9
0
        /// <param name="projectsToInclude">Cone of projects to compute a checksum for.  Pass in <see langword="null"/>
        /// to get a checksum for the entire solution</param>
        private async Task <SolutionStateChecksums> ComputeChecksumsAsync(
            HashSet <ProjectId>?projectsToInclude,
            SerializableOptionSet options,
            CancellationToken cancellationToken)
        {
            try
            {
                using (Logger.LogBlock(FunctionId.SolutionState_ComputeChecksumsAsync, FilePath, cancellationToken))
                {
                    // get states by id order to have deterministic checksum.  Limit to the requested set of projects
                    // if applicable.
                    var orderedProjectIds    = ChecksumCache.GetOrCreate(ProjectIds, _ => ProjectIds.OrderBy(id => id.Id).ToImmutableArray());
                    var projectChecksumTasks = orderedProjectIds.Where(id => projectsToInclude == null || projectsToInclude.Contains(id))
                                               .Select(id => ProjectStates[id])
                                               .Where(s => RemoteSupportedLanguages.IsSupported(s.Language))
                                               .Select(s => s.GetChecksumAsync(cancellationToken))
                                               .ToArray();

                    var serializer         = _solutionServices.Workspace.Services.GetRequiredService <ISerializerService>();
                    var attributesChecksum = serializer.CreateChecksum(SolutionAttributes, cancellationToken);

                    var optionsChecksum = serializer.CreateChecksum(options, cancellationToken);

                    var frozenSourceGeneratedDocumentIdentityChecksum = Checksum.Null;
                    var frozenSourceGeneratedDocumentTextChecksum     = Checksum.Null;

                    if (FrozenSourceGeneratedDocumentState != null)
                    {
                        frozenSourceGeneratedDocumentIdentityChecksum = serializer.CreateChecksum(FrozenSourceGeneratedDocumentState.Identity, cancellationToken);
                        frozenSourceGeneratedDocumentTextChecksum     = (await FrozenSourceGeneratedDocumentState.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false)).Text;
                    }

                    var analyzerReferenceChecksums = ChecksumCache.GetOrCreate <ChecksumCollection>(AnalyzerReferences,
                                                                                                    _ => new ChecksumCollection(AnalyzerReferences.Select(r => serializer.CreateChecksum(r, cancellationToken)).ToArray()));

                    var projectChecksums = await Task.WhenAll(projectChecksumTasks).ConfigureAwait(false);

                    return(new SolutionStateChecksums(attributesChecksum, optionsChecksum, new ChecksumCollection(projectChecksums), analyzerReferenceChecksums, frozenSourceGeneratedDocumentIdentityChecksum, frozenSourceGeneratedDocumentTextChecksum));
                }
            }
            catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
示例#10
0
        private async Task <SolutionStateChecksums> ComputeChecksumsAsync(CancellationToken cancellationToken)
        {
            using (Logger.LogBlock(FunctionId.SolutionState_ComputeChecksumsAsync, FilePath, cancellationToken))
            {
                // get states by id order to have deterministic checksum
                var orderedProjectIds    = ChecksumCache.GetOrCreate(ProjectIds, _ => ProjectIds.OrderBy(id => id.Id).ToImmutableArray());
                var projectChecksumTasks = orderedProjectIds.Select(id => ProjectStates[id])
                                           .Where(s => RemoteSupportedLanguages.IsSupported(s.Language))
                                           .Select(s => s.GetChecksumAsync(cancellationToken));

                var serializer   = _solutionServices.Workspace.Services.GetService <ISerializerService>();
                var infoChecksum = serializer.CreateChecksum(SolutionAttributes, cancellationToken);

                var projectChecksums = await Task.WhenAll(projectChecksumTasks).ConfigureAwait(false);

                return(new SolutionStateChecksums(infoChecksum, new ProjectChecksumCollection(projectChecksums)));
            }
        }
示例#11
0
        public static async Task <Checksum> GetChecksumAsync(
            Document document, CancellationToken cancellationToken)
        {
            // Since we build the SyntaxTreeIndex from a SyntaxTree, we need our checksum to change
            // any time the SyntaxTree could have changed.  Right now, that can only happen if the
            // text of the document changes, or the ParseOptions change.  So we get the checksums
            // for both of those, and merge them together to make the final checksum.

            var documentChecksumState = await document.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false);

            var textChecksum = documentChecksumState.Text;

            var parseOptions         = document.Project.ParseOptions;
            var serializer           = new Serializer(document.Project.Solution.Workspace);
            var parseOptionsChecksum = ChecksumCache.GetOrCreate(
                parseOptions, _ => serializer.CreateChecksum(parseOptions, cancellationToken));

            return(Checksum.Create(WellKnownSynchronizationKind.SyntaxTreeIndex, new[] { textChecksum, parseOptionsChecksum }));
        }
        /// <param name="projectsToInclude">Cone of projects to compute a checksum for.  Pass in <see langword="null"/>
        /// to get a checksum for the entire solution</param>
        private async Task <SolutionStateChecksums> ComputeChecksumsAsync(
            HashSet <ProjectId>?projectsToInclude,
            SerializableOptionSet options,
            CancellationToken cancellationToken)
        {
            try
            {
                using (Logger.LogBlock(FunctionId.SolutionState_ComputeChecksumsAsync, FilePath, cancellationToken))
                {
                    // get states by id order to have deterministic checksum.  Limit expensive computation to the
                    // requested set of projects if applicable.
                    var orderedProjectIds    = ChecksumCache.GetOrCreate(ProjectIds, _ => ProjectIds.OrderBy(id => id.Id).ToImmutableArray());
                    var projectChecksumTasks = orderedProjectIds
                                               .Select(id => (state: ProjectStates[id], mustCompute: projectsToInclude == null || projectsToInclude.Contains(id)))
                                               .Where(t => RemoteSupportedLanguages.IsSupported(t.state.Language))
                                               .Select(async t =>
                    {
                        // if it's a project that's specifically in the sync'ed cone, include this checksum so that
                        // this project definitely syncs over.
                        if (t.mustCompute)
                        {
                            return(await t.state.GetChecksumAsync(cancellationToken).ConfigureAwait(false));
                        }

                        // If it's a project that is not in the cone, still try to get the latest checksum for it if
                        // we have it.  That way we don't send over a checksum *without* that project, causing the
                        // OOP side to throw that project away (along with all the compilation info stored with it).
                        if (t.state.TryGetStateChecksums(out var stateChecksums))
                        {
                            return(stateChecksums.Checksum);
                        }

                        // We have never computed the checksum for this project.  Don't send anything for it.
                        return(null);
                    })
                                               .ToArray();

                    var serializer         = _solutionServices.Workspace.Services.GetRequiredService <ISerializerService>();
                    var attributesChecksum = serializer.CreateChecksum(SolutionAttributes, cancellationToken);

                    var optionsChecksum = serializer.CreateChecksum(options, cancellationToken);

                    var frozenSourceGeneratedDocumentIdentityChecksum = Checksum.Null;
                    var frozenSourceGeneratedDocumentTextChecksum     = Checksum.Null;

                    if (FrozenSourceGeneratedDocumentState != null)
                    {
                        frozenSourceGeneratedDocumentIdentityChecksum = serializer.CreateChecksum(FrozenSourceGeneratedDocumentState.Identity, cancellationToken);
                        frozenSourceGeneratedDocumentTextChecksum     = (await FrozenSourceGeneratedDocumentState.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false)).Text;
                    }

                    var analyzerReferenceChecksums = ChecksumCache.GetOrCreate <ChecksumCollection>(AnalyzerReferences,
                                                                                                    _ => new ChecksumCollection(AnalyzerReferences.Select(r => serializer.CreateChecksum(r, cancellationToken)).ToArray()));

                    var projectChecksums = await Task.WhenAll(projectChecksumTasks).ConfigureAwait(false);

                    return(new SolutionStateChecksums(
                               attributesChecksum,
                               optionsChecksum,
                               new ChecksumCollection(projectChecksums.WhereNotNull().ToArray()),
                               analyzerReferenceChecksums,
                               frozenSourceGeneratedDocumentIdentityChecksum,
                               frozenSourceGeneratedDocumentTextChecksum));
                }
            }
            catch (Exception e) when(FatalError.ReportAndPropagateUnlessCanceled(e, cancellationToken))
            {
                throw ExceptionUtilities.Unreachable;
            }
        }
示例#13
0
        private async void VerifyFiles_Click(object sender, RoutedEventArgs e)
        {
            //*/
            try
            {
                this.EnsureThings();

                string cachePath = this.checksumcache_path.Text,
                       pso2dir   = this.pso2directory_path.Text;

                if (!File.Exists(Path.Combine(pso2dir, "pso2launcher.exe")) && !File.Exists(Path.Combine(pso2dir, "pso2.exe")))
                {
                    if (await this.MsgBoxYesNo("PSO2 Directory setting seems to not point to the 'pso2_dir' directory.\nAre you sure you still want to continue?", "Confirmation", "Yes, continue", "Nope") != MessageDialogResult.Affirmative)
                    {
                        return;
                    }
                }

                if (await this.MsgBoxYesNo("Are you sure you want to verify the whole game client?", "Question") == MessageDialogResult.Affirmative)
                {
                    UpdaterProfile profile;
                    ComboBoxItem   item = this.UpdaterProfile.SelectedItem as ComboBoxItem;
                    if (item == null)
                    {
                        profile = Leayal.PSO2.Updater.UpdaterProfile.Balanced;
                    }
                    else
                    {
                        profile = (Leayal.PSO2.Updater.UpdaterProfile)item.Tag;
                    }
                    int threadcount = Math.Min(Environment.ProcessorCount, 4);
                    item = this.maxDegreeOfParallelism.SelectedItem as ComboBoxItem;
                    if (item != null)
                    {
                        threadcount = (int)item.Tag;
                    }
                    this.progressStep.Text           = "Preparing";
                    this.progressbar.IsIndeterminate = true;
                    this.tab_Progress.IsSelected     = true;
                    bool usecache = (this.checksumcache_use.IsChecked == true);

                    this.config.SetValue("Cache", "Filepath", cachePath);
                    this.config.SetValue("PSO2", "Directory", pso2dir);

                    var version = await this.updater.GetPatchManagementAsync();

                    Action <ClientUpdateOptions> continueAction = (result) =>
                    {
                        this.Dispatcher.BeginInvoke(new JustAction(() =>
                        {
                            this.progressStep.Text             = "Preparing patchlist";
                            this.progressbar.IsIndeterminate   = false;
                            this.TaskbarItemInfo.ProgressState = System.Windows.Shell.TaskbarItemProgressState.Normal;
                        }), DispatcherPriority.Normal, null);
                        if (this.downloadingfiles == null)
                        {
                            this.downloadingfiles = new ConcurrentDictionary <string, bool>();
                        }
                        else
                        {
                            this.downloadingfiles.Clear();
                        }

                        this.currentstep = "Downloading patchlists";
                        Task.Run(async() =>
                        {
                            RemotePatchlist patchlist = await this.updater.GetPatchlistAsync(version);
                            await this.updater.VerifyAndDownloadAsync(pso2dir, version, patchlist, result);
                        });
                    };

                    await Task.Run(() =>
                    {
                        ClientUpdateOptions options = new ClientUpdateOptions()
                        {
                            Profile = profile,
                            MaxDegreeOfParallelism = threadcount
                        };
                        if ((usecache == true) && !string.IsNullOrWhiteSpace(cachePath))
                        {
                            if (File.Exists(cachePath))
                            {
                                try
                                {
                                    var checksumCache = ChecksumCache.OpenFromFile(cachePath);
                                    if (!string.Equals(checksumCache.PSO2Version, version.CurrentVersion, StringComparison.OrdinalIgnoreCase))
                                    {
                                        this.Dispatcher.BeginInvoke(new JustAction(async() =>
                                        {
                                            var answer = await this.MsgBoxYesNoCancel($"The cache you provided is for PSO2 client ver {checksumCache.PSO2Version} while your current client version is {version.CurrentVersion}. ?", "Question", "Skip cache", "Rebuild cache", "Cancel operation");
                                            if (answer == MessageDialogResult.Affirmative)
                                            {
                                                checksumCache.Dispose();
                                            }
                                            else if (answer == MessageDialogResult.Negative)
                                            {
                                                if (options.Profile == Leayal.PSO2.Updater.UpdaterProfile.PreferSpeed)
                                                {
                                                    options.Profile = Leayal.PSO2.Updater.UpdaterProfile.Balanced;
                                                }
                                                checksumCache.ChecksumList.Clear();
                                                options.ChecksumCache = checksumCache;
                                            }
                                            else
                                            {
                                                checksumCache.Dispose();
                                                this.tab_Mainmenu.IsSelected = true;
                                                return;
                                            }
                                            continueAction.Invoke(options);
                                        }), DispatcherPriority.Normal, null);
                                        return;
                                    }
                                    else
                                    {
                                        options.ChecksumCache = checksumCache;
                                    }
                                }
                                catch (InvalidCacheException)
                                {
                                    options.ChecksumCache = ChecksumCache.Create(cachePath);
                                }
                            }
                            else
                            {
                                options.ChecksumCache = ChecksumCache.Create(cachePath);
                            }
                        }
                        continueAction.Invoke(options);
                    });
                }
                else
                {
                    this.tab_Mainmenu.IsSelected = true;
                }
            }
#if !DEBUG
            catch (WrappedWarningException warn)
            {
                this.tab_Mainmenu.IsSelected = true;
                await this.MsgBoxOK(warn.Message, "Warning");
            }
#endif
            catch (Exception ex)
            {
                this.tab_Mainmenu.IsSelected = true;
                MessageBox.Show(this, ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
示例#14
0
        private void Updater_StepChanged(UpdateStep arg1, object arg2)
        {
            switch (arg1)
            {
            case UpdateStep.DownloadingFileStart:
                if (arg2 is PSO2File file)
                {
                    this.downloadingfiles.TryAdd(file.SafeFilename, true);
                    if (this.downloadingfiles.Count == 1)
                    {
                        this.Dispatcher.BeginInvoke(new JustActionOneArg((x) =>
                        {
                            this.downloadingStep.Text = $"Downloading:\n{x}";
                        }), DispatcherPriority.Normal, this.downloadingfiles.First().Key);
                    }
                    else
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.Append("Downloading:");
                        foreach (string filename in this.downloadingfiles.Keys)
                        {
                            sb.Append("\n");
                            sb.Append(filename);
                        }
                        this.Dispatcher.BeginInvoke(new JustActionOneArg((x) =>
                        {
                            this.downloadingStep.Text = (string)x;
                        }), DispatcherPriority.Normal, sb.ToString());
                    }
                }
                break;

            case UpdateStep.DownloadingFileEnd:
                if (arg2 is PSO2File file2)
                {
                    this.downloadingfiles.TryRemove(file2.SafeFilename, out var somebool);
                    if (this.downloadingfiles.Count == 0)
                    {
                        this.Dispatcher.BeginInvoke(new JustAction(() =>
                        {
                            this.downloadingStep.Text = string.Empty;
                        }), DispatcherPriority.Normal, null);
                    }
                    else if (this.downloadingfiles.Count == 1)
                    {
                        string va = this.downloadingfiles.First().Key;
                        this.Dispatcher.BeginInvoke(new JustActionOneArg((x) =>
                        {
                            this.downloadingStep.Text = $"Downloading:\n{x}";
                        }), DispatcherPriority.Normal, this.downloadingfiles.First().Key);
                    }
                    else
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.Append("Downloading:");
                        foreach (string filename in this.downloadingfiles.Keys)
                        {
                            sb.Append("\n");
                            sb.Append(filename);
                        }
                        this.Dispatcher.BeginInvoke(new JustActionOneArg((x) =>
                        {
                            this.downloadingStep.Text = (string)x;
                        }), DispatcherPriority.Normal, sb.ToString());
                    }
                }
                break;

            case UpdateStep.BeginFileCheckAndDownload:
                this.currentstep = "Verifying files";
                break;

            case UpdateStep.WriteCache:
                this.Dispatcher.BeginInvoke(new JustActionOneArg((x) =>
                {
                    ChecksumCache cache = x as ChecksumCache;
                    if (cache != null)
                    {
                        if (cache.ChecksumList.Count == 1)
                        {
                            this.progressStep.Text = "Writing 1 entry to the cache file.";
                        }
                        else
                        {
                            this.progressStep.Text = $"Writing {cache.ChecksumList.Count} entries to the cache file.";
                        }
                    }
                }), DispatcherPriority.Normal, arg2);
                break;
            }
        }