1+ use rayon:: iter:: ParallelIterator ;
2+ use rayon:: iter:: IndexedParallelIterator ;
13use std:: path:: { Path , PathBuf } ;
2-
4+ use std :: sync :: { Arc , Mutex } ;
35use itertools:: Itertools ;
6+ use rayon:: prelude:: IntoParallelRefIterator ;
47use thiserror:: Error ;
58use crate :: resource:: partition_manager:: PartitionManagerError :: PartitionNotFound ;
69
@@ -31,6 +34,9 @@ pub enum PartitionManagerError {
3134 #[ error( "Could not discover game paths: {0}" ) ]
3235 GameDiscoveryError ( #[ from] GameDiscoveryError ) ,
3336
37+ #[ error( "Could not locate runtime directory: {0}" ) ]
38+ RuntimeDirectoryNotFound ( PathBuf ) ,
39+
3440 #[ error( "Could not find a root partition" ) ]
3541 NoRootPartition ( ) ,
3642}
@@ -49,6 +55,29 @@ pub struct PartitionManager {
4955 pub partitions : Vec < ResourcePartition > , //All mounted partitions
5056}
5157
58+ #[ cfg( feature = "rayon" ) ]
59+ pub trait PartitionManagerPar {
60+ fn from_game_par (
61+ retail_directory : PathBuf ,
62+ game_version : WoaVersion ,
63+ mount : bool ,
64+ ) -> Result < Self , PartitionManagerError > where Self : Sized ;
65+
66+ fn from_game_with_callback_par < F > (
67+ retail_directory : PathBuf ,
68+ game_version : WoaVersion ,
69+ mount : bool ,
70+ progress_callback : F ,
71+ ) -> Result < Self , PartitionManagerError >
72+ where
73+ F : FnMut ( usize , & PartitionState ) + Send + Sync , Self : Sized ;
74+
75+ fn mount_partitions_par < F > ( & mut self , progress_callback : F ) -> Result < ( ) , PartitionManagerError >
76+ where
77+ F : FnMut ( usize , & PartitionState ) + Send + Sync ;
78+
79+ }
80+
5281impl PartitionManager {
5382 /// Create a new PartitionManager for the game at the given path, and a custom package definition.
5483 ///
@@ -59,6 +88,11 @@ impl PartitionManager {
5988 runtime_directory : PathBuf ,
6089 package_definition : & PackageDefinitionSource ,
6190 ) -> Result < Self , PartitionManagerError > {
91+
92+ if !runtime_directory. exists ( ) {
93+ return Err ( PartitionManagerError :: RuntimeDirectoryNotFound ( runtime_directory) ) ;
94+ }
95+
6296 let partition_infos = package_definition
6397 . read ( )
6498 . map_err ( PartitionManagerError :: PackageDefinitionError ) ?;
@@ -419,3 +453,94 @@ impl PartitionManager {
419453 }
420454 }
421455}
456+
457+ #[ cfg( feature = "rayon" ) ]
458+ impl PartitionManagerPar for PartitionManager {
459+ /// Create a new PartitionManager by mounting the game at the given path, but parallel.
460+ ///
461+ /// # Arguments
462+ /// - `retail_path` - The path to the game's retail directory.
463+ /// - `game_version` - The version of the game.
464+ /// - `mount` - Indicates whether to automatically mount the partitions, can eliminate the need to call `mount_partitions_par` separately
465+ fn from_game_par (
466+ retail_directory : PathBuf ,
467+ game_version : WoaVersion ,
468+ mount : bool ,
469+ ) -> Result < Self , PartitionManagerError > {
470+ Self :: from_game_with_callback_par ( retail_directory, game_version, mount, |_, _| { } )
471+ }
472+
473+ /// Create a new PartitionManager by mounting the game at the given path, but parallel.
474+ ///
475+ /// # Arguments
476+ /// - `retail_path` - The path to the game's retail directory.
477+ /// - `game_version` - The version of the game.
478+ /// - `mount` - Indicates whether to automatically mount the partitions, can eliminate the need to call `mount_partitions_par` separately
479+ /// - `progress_callback` - A callback function that will be called with the current mounting progress.
480+ fn from_game_with_callback_par < F > (
481+ retail_directory : PathBuf ,
482+ game_version : WoaVersion ,
483+ mount : bool ,
484+ progress_callback : F ,
485+ ) -> Result < Self , PartitionManagerError >
486+ where
487+ F : FnMut ( usize , & PartitionState ) + Send + Sync ,
488+ {
489+ let game_paths = GamePaths :: from_retail_directory ( retail_directory) ?;
490+ let package_definition =
491+ PackageDefinitionSource :: from_file ( game_paths. package_definition_path , game_version) ?;
492+
493+ // And read all the partition infos.
494+ let partition_infos = package_definition
495+ . read ( )
496+ . map_err ( PartitionManagerError :: PackageDefinitionError ) ?;
497+
498+ let mut package_manager = Self {
499+ runtime_directory : game_paths. runtime_path ,
500+ partition_infos,
501+ partitions : vec ! [ ] ,
502+ } ;
503+
504+ // If the user requested auto mounting, do it.
505+ if mount {
506+ package_manager. mount_partitions_par ( progress_callback) ?;
507+ }
508+
509+ Ok ( package_manager)
510+ }
511+
512+ /// Mount all the partitions in the game, parallelly.
513+ ///
514+ /// # Arguments
515+ /// - `progress_callback` - A callback function that will be called with the current mounting progress.
516+ fn mount_partitions_par < F > ( & mut self , progress_callback : F ) -> Result < ( ) , PartitionManagerError >
517+ where
518+ F : FnMut ( usize , & PartitionState ) + Send + Sync ,
519+ {
520+ let progress_callback = Arc :: new ( Mutex :: new ( progress_callback) ) ;
521+
522+ let runtime_directory = self . runtime_directory . clone ( ) ; // Clone if needed
523+
524+ let results: Result < Vec < _ > , PartitionManagerError > = self . partition_infos
525+ . par_iter ( )
526+ . enumerate ( )
527+ . map ( |( index, partition_info) | {
528+ Self :: try_read_partition ( & runtime_directory, partition_info. clone ( ) , |state| {
529+ let mut cb = progress_callback. lock ( ) . unwrap ( ) ;
530+ cb ( index, state)
531+ } )
532+ } )
533+ . filter_map ( |result| match result {
534+ Ok ( Some ( partition) ) => Some ( Ok ( partition) ) ,
535+ Ok ( None ) => None ,
536+ Err ( e) => Some ( Err ( e) ) ,
537+ } )
538+ . collect ( ) ;
539+
540+ for partition in results? {
541+ self . partitions . push ( partition) ;
542+ }
543+
544+ Ok ( ( ) )
545+ }
546+ }
0 commit comments