Ejemplo n.º 1
0
        /// <summary>
        /// Attempts to load a game in a specified path, returning a <see cref="PackageGroup"/> object with the
        /// packages in the path and an <see cref="SerializedTree"/> with a fully qualified node tree of the
        /// package group.
        /// If no packages are found, then this method will return null in both fields.
        /// </summary>
        /// <param name="gameAlias">The alias of the game at the path.</param>
        /// <param name="gamePath">The path to load as a game.</param>
        /// <param name="ct">A cancellation token.</param>
        /// <param name="progress">An <see cref="IProgress{GameLoadingProgress}"/> object for progress reporting.</param>
        /// <returns>A tuple with a package group and a node tree for the requested game.</returns>
        public async Task <(PackageGroup?packageGroup, SerializedTree?nodeTree)> LoadGameAsync
        (
            string gameAlias,
            string gamePath,
            CancellationToken ct,
            IProgress <GameLoadingProgress>?progress = null
        )
        {
            progress?.Report(new GameLoadingProgress
            {
                CompletionPercentage = 0.0f,
                State = GameLoadingState.SettingUp,
                Alias = gameAlias
            });

            var packagePaths = Directory.EnumerateFiles
                               (
                gamePath,
                "*",
                SearchOption.AllDirectories
                               )
                               .Where(p => p.EndsWith(".mpq", StringComparison.InvariantCultureIgnoreCase))
                               .OrderBy(p => p)
                               .ToList();

            if (packagePaths.Count == 0)
            {
                return(null, null);
            }

            var packageSetHash      = GeneratePathSetHash(packagePaths);
            var packageTreeFilename = $".{packageSetHash}.tree";
            var packageTreeFilePath = Path.Combine(gamePath, packageTreeFilename);

            var            packageGroup = new PackageGroup(packageSetHash);
            SerializedTree?nodeTree     = null;

            var generateTree = true;

            if (File.Exists(packageTreeFilePath))
            {
                progress?.Report(new GameLoadingProgress
                {
                    CompletionPercentage = 0,
                    State = GameLoadingState.LoadingNodeTree,
                    Alias = gameAlias
                });

                try
                {
                    // Load tree
                    nodeTree     = new SerializedTree(File.OpenRead(packageTreeFilePath));
                    generateTree = false;
                }
                catch (FileNotFoundException)
                {
                    Log.Error("No file for the node tree found at the given location.");
                }
                catch (NotSupportedException)
                {
                    Log.Info("Unsupported node tree version present. Deleting and regenerating.");
                    File.Delete(packageTreeFilePath);
                }
            }

            if (generateTree)
            {
                // Internal counters for progress reporting
                double completedSteps = 0;
                double totalSteps     = packagePaths.Count * 2;

                // Load packages
                var packages = new List <(string packageName, IPackage package)>();
                foreach (var packagePath in packagePaths)
                {
                    ct.ThrowIfCancellationRequested();

                    progress?.Report(new GameLoadingProgress
                    {
                        CompletionPercentage = completedSteps / totalSteps,
                        State = GameLoadingState.LoadingPackages,
                        Alias = gameAlias
                    });

                    try
                    {
                        var package = await PackageInteractionHandler.LoadAsync(packagePath);

                        packages.Add((Path.GetFileNameWithoutExtension(packagePath), package));
                    }
                    catch (FileLoadException fex)
                    {
                        Log.Warn($"Failed to load archive {Path.GetFileNameWithoutExtension(packagePath)}: {fex.Message}");
                    }

                    ++completedSteps;
                }

                // Load dictionary if neccesary
                if (_dictionary == null)
                {
                    progress?.Report(new GameLoadingProgress
                    {
                        CompletionPercentage = completedSteps / totalSteps,
                        State = GameLoadingState.LoadingDictionary,
                        Alias = gameAlias
                    });

                    _dictionary = await LoadDictionaryAsync(ct);
                }

                // Generate node tree
                var builder = new TreeBuilder();
                foreach (var packageInfo in packages)
                {
                    ct.ThrowIfCancellationRequested();

                    progress?.Report(new GameLoadingProgress
                    {
                        CompletionPercentage = completedSteps / totalSteps,
                        State = GameLoadingState.BuildingNodeTree,
                        Alias = gameAlias
                    });

                    var steps = completedSteps;
                    var createNodesProgress = new Progress <PackageNodesCreationProgress>
                                              (
                        p =>
                    {
                        progress?.Report
                        (
                            new GameLoadingProgress
                        {
                            CompletionPercentage = steps / totalSteps,
                            State                 = GameLoadingState.BuildingNodeTree,
                            Alias                 = gameAlias,
                            CurrentPackage        = packageInfo.packageName,
                            NodesCreationProgress = p
                        }
                        );
                    }
                                              );

                    await Task.Run(() => builder.AddPackage(packageInfo.packageName, packageInfo.package, createNodesProgress, ct), ct);

                    packageGroup.AddPackage((PackageInteractionHandler)packageInfo.package);

                    ++completedSteps;
                }

                // Build node tree
                var tree = builder.GetTree();

                var optimizeTreeProgress = new Progress <TreeOptimizationProgress>
                                           (
                    p =>
                {
                    progress?.Report
                    (
                        new GameLoadingProgress
                    {
                        CompletionPercentage = completedSteps / totalSteps,
                        State = GameLoadingState.BuildingNodeTree,
                        Alias = gameAlias,
                        OptimizationProgress = p
                    }
                    );
                }
                                           );

                var optimizer = new TreeOptimizer(_dictionary);

                var treeClosureCopy = tree;
                tree = await Task.Run(() => optimizer.OptimizeTree(treeClosureCopy, optimizeTreeProgress, ct), ct);

                using (var fs = File.OpenWrite(packageTreeFilePath))
                {
                    using (var serializer = new TreeSerializer(fs))
                    {
                        await serializer.SerializeAsync(tree, ct);
                    }
                }

                nodeTree = new SerializedTree(File.OpenRead(packageTreeFilePath));
            }
            else
            {
                progress?.Report(new GameLoadingProgress
                {
                    CompletionPercentage = 1,
                    State = GameLoadingState.LoadingPackages,
                    Alias = gameAlias
                });

                // Load packages
                packageGroup = await PackageGroup.LoadAsync(gameAlias, packageSetHash, gamePath, ct, progress);
            }

            progress?.Report(new GameLoadingProgress
            {
                CompletionPercentage = 1,
                State = GameLoadingState.Loading,
                Alias = gameAlias
            });

            return(packageGroup, nodeTree);
        }