Skip to content

Commit 3a8ab32

Browse files
committed
Basic rayon feature
1 parent cf38656 commit 3a8ab32

2 files changed

Lines changed: 130 additions & 3 deletions

File tree

Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ version = "1.2.0"
44
edition = "2021"
55
license = "Apache-2.0"
66
categories = ["game-development", "data-structures", "parser-implementations"]
7-
keywords = ["game-archive", "hitman", "glacier", "rpkg", "moddng"]
7+
keywords = ["game-archive", "hitman", "glacier", "rpkg", "modding"]
88
description = "Parse Glacier ResourcePackage (rpkg) files, allowing access to the resources stored within."
99
repository = "https://github.com/dafitius/rpkg-rs"
1010
readme = "README.md"
@@ -29,11 +29,13 @@ serde = { version = "1.0.217", optional = true, features = ["derive"] }
2929
serde-hex = { version = "0.1.0", optional = true }
3030
indexmap = "2.7.1"
3131
crc32fast = "1.4.2"
32+
async-trait = { version = "0.1.88", optional = true}
3233

3334
[features]
34-
default = ["path-list", "serde"]
35+
default = ["rayon", "path-list", "serde"]
3536
path-list = ["dep:rayon"]
3637
serde = ["dep:serde", "dep:serde-hex"]
38+
rayon = ["dep:rayon"]
3739

3840
[dev-dependencies]
3941
serde_json = "1.0.128"

src/resource/partition_manager.rs

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
use rayon::iter::ParallelIterator;
2+
use rayon::iter::IndexedParallelIterator;
13
use std::path::{Path, PathBuf};
2-
4+
use std::sync::{Arc, Mutex};
35
use itertools::Itertools;
6+
use rayon::prelude::IntoParallelRefIterator;
47
use thiserror::Error;
58
use 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+
5281
impl 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

Comments
 (0)