Esempio n. 1
0
        /// <summary>
        /// Loads the listfile for the specifed package and adds it to the local list of packages.
        /// </summary>
        /// <returns>
        /// Returns <value>false</value> if the package didn't have a listfile or if the listfile couldn't be loaded,
        /// <value>true</value> otherwise.
        /// </returns>
        public bool LoadListfileByPackage(PackageInteractionHandler inPackageHandler)
        {
            if (!HasBundledListfiles())
            {
                // We don't have any listfiles, so there's no use
                return(false);
            }

            if (this.OptimizedLists.ContainsKey(inPackageHandler.PackageName))
            {
                // The package listfile container has already been loaded.
                // Thus, check if the container has a listfile for this package
                return(HasListfileForPackage(inPackageHandler));
            }

            foreach (string bundledListfilePath in GetAvailableBundledListfilePaths())
            {
                if (Path.GetFileNameWithoutExtension(bundledListfilePath) == inPackageHandler.PackageName)
                {
                    OptimizedListContainer bundledListfile = new OptimizedListContainer(File.ReadAllBytes(bundledListfilePath));

                    // Keep the listfile container around, in case we need it.
                    this.OptimizedLists.Add(bundledListfile.PackageName, bundledListfile);

                    if (bundledListfile.ContainsPackageListfile(inPackageHandler.GetHashTableHash()))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
Esempio n. 2
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);
        }
Esempio n. 3
0
 /// <summary>
 /// Determines whether the provided package handler has a bundled listfile.
 /// </summary>
 /// <returns><c>true</c> if this instance has listfile for package the specified PackageHandler; otherwise, <c>false</c>.</returns>
 /// <param name="inPackageHandler">Package handler.</param>
 public bool HasListfileForPackage(PackageInteractionHandler inPackageHandler)
 {
     return(HasListfileForPackage(inPackageHandler.PackageName, inPackageHandler.GetHashTableHash()));
 }
Esempio n. 4
0
 /// <summary>
 /// Gets the bundled listfile for the provided package handler.
 /// </summary>
 /// <returns>The bundled listfile.</returns>
 /// <param name="inPackageHandler">Package handler.</param>
 public List <string> GetBundledListfile(PackageInteractionHandler inPackageHandler)
 {
     return(GetBundledListfile(inPackageHandler.PackageName, inPackageHandler.GetHashTableHash()));
 }
Esempio n. 5
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="OptimizedNodeTree"/> 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, OptimizedNodeTree nodeTree)> LoadGameAsync(
            string gameAlias,
            string gamePath,
            CancellationToken ct,
            IProgress <GameLoadingProgress> progress = null)
        {
            progress?.Report(new GameLoadingProgress
            {
                CompletionPercentage = 0.0f,
                State = GameLoadingState.SettingUp,
                Alias = gameAlias
            });

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

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

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

            PackageGroup      packageGroup = new PackageGroup(packageSetHash);
            OptimizedNodeTree nodeTree     = null;

            bool generateTree = true;

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

                try
                {
                    // Load tree
                    nodeTree     = new OptimizedNodeTree(packageTreeFilePath);
                    generateTree = false;
                }
                catch (NodeTreeNotFoundException)
                {
                    Log.Error("No file for the node tree found at the given location.");
                }
                catch (UnsupportedNodeTreeVersionException)
                {
                    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
                List <(string packageName, IPackage package)> packages = new List <(string packageName, IPackage package)>();
                foreach (string packagePath in packagePaths)
                {
                    ct.ThrowIfCancellationRequested();

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

                    try
                    {
                        PackageInteractionHandler 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 (this.Dictionary == null)
                {
                    progress?.Report(new GameLoadingProgress
                    {
                        CompletionPercentage = completedSteps / totalSteps,
                        State = GameLoadingState.LoadingDictionary,
                        Alias = gameAlias
                    });

                    this.Dictionary = await LoadDictionaryAsync(ct);
                }

                // Generate node tree
                MultiPackageNodeTreeBuilder multiBuilder = new MultiPackageNodeTreeBuilder(this.Dictionary);
                foreach (var packageInfo in packages)
                {
                    ct.ThrowIfCancellationRequested();

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

                    await multiBuilder.ConsumePackageAsync(packageInfo.packageName, packageInfo.package, ct);

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

                    ++completedSteps;
                }

                // Build node tree
                multiBuilder.Build();

                // Save it to disk
                File.WriteAllBytes(packageTreeFilePath, multiBuilder.CreateTree());

                nodeTree = new OptimizedNodeTree(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);
        }