/// <summary> /// Loads the bundled dictionary from disk. /// </summary> /// <param name="ct">A cancellation to open.</param> /// <returns>A loaded ListfileDictionary.</returns> private static Task <ListfileDictionary> LoadDictionaryAsync(CancellationToken ct) { return(Task.Run ( () => { var dict = new ListfileDictionary(); dict.LoadFromStream(File.OpenRead("Dictionary/dictionary.dic"), ct); return dict; }, ct )); }
/// <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); }