Skip to content

Commit

Permalink
optimize and parallelize IO and hashing
Browse files Browse the repository at this point in the history
  • Loading branch information
iliazeus committed Oct 13, 2024
1 parent c247155 commit e8fff7a
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 112 deletions.
98 changes: 75 additions & 23 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "iso2god"
version = "1.6.0"
version = "1.7.0"
description = "A tool to convert between Xbox 360 ISO and Games On Demand file formats"
repository = "https://github.com/iliazeus/iso2god-rs"
edition = "2021"
Expand All @@ -13,6 +13,7 @@ bitflags = "2.6.0"
byteorder = "1.5.0"
clap = { version = "4.5.19", features = ["derive"] }
num_enum = "0.7.3"
rayon = "1.10.0"
sha1 = "0.10.6"

[dev-dependencies]
Expand Down
64 changes: 37 additions & 27 deletions src/bin/iso2god.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use std::io::{BufReader, BufWriter, Read, Seek, Write};
use std::io::{BufReader, Seek, SeekFrom, Write};

use std::fs;
use std::fs::File;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicUsize, Ordering};

use anyhow::{Context, Error};

use clap::{arg, command, Parser};

use rayon::prelude::*;

use iso2god::executable::TitleInfo;
use iso2god::god::ContentType;
use iso2god::{game_list, god, iso};
Expand Down Expand Up @@ -37,6 +40,10 @@ struct Cli {
/// Trim off unused space from the ISO image
#[arg(long)]
trim: bool,

/// Number of worker threads to use
#[arg(long, short = 'j')]
num_threads: Option<usize>,
}

fn main() -> Result<(), Error> {
Expand All @@ -47,10 +54,13 @@ fn main() -> Result<(), Error> {
eprintln!("the --offline flag is deprecated: the tool now has a built-in title database, so it is always offline");
}

rayon::ThreadPoolBuilder::new()
.num_threads(args.num_threads.unwrap_or(0))
.build_global()?;

println!("extracting ISO metadata");

let source_iso_file = open_file_for_buffered_reading(&args.source_iso)
.context("error opening source ISO file")?;
let source_iso_file = File::open(&args.source_iso).context("error opening source ISO file")?;

let source_iso_file_meta =
fs::metadata(&args.source_iso).context("error reading source ISO file metadata")?;
Expand Down Expand Up @@ -96,23 +106,31 @@ fn main() -> Result<(), Error> {

ensure_empty_dir(&file_layout.data_dir_path()).context("error clearing data directory")?;

let mut source_iso = source_iso
.get_root()
.context("error reading source iso")?
.take(data_size);
println!("writing part files: 0/{part_count}");

println!("writing part files");
let progress = AtomicUsize::new(0);

for part_index in 0..part_count {
println!("writing part {:2} of {:2}", part_index, part_count);
(0..part_count).into_par_iter().try_for_each(|part_index| {
let mut iso_data_volume = File::open(&args.source_iso)?;
iso_data_volume.seek(SeekFrom::Start(source_iso.volume_descriptor.root_offset))?;

let part_file = file_layout.part_file_path(part_index);

let mut part_file =
open_file_for_buffered_writing(&part_file).context("error creating part file")?;
let part_file = File::options()
.write(true)
.create(true)
.truncate(true)
.open(&part_file)
.context("error creating part file")?;

god::write_part(&mut source_iso, &mut part_file).context("error writing part file")?;
}
god::write_part(iso_data_volume, part_index, part_file)
.context("error writing part file")?;

let cur = 1 + progress.fetch_add(1, Ordering::Relaxed);
println!("writing part files: {cur:2}/{part_count}");

Ok::<_, anyhow::Error>(())
})?;

println!("calculating MHT hash chain");

Expand Down Expand Up @@ -156,7 +174,11 @@ fn main() -> Result<(), Error> {

let con_header = con_header.finalize();

let mut con_header_file = open_file_for_buffered_writing(&file_layout.con_header_file_path())
let mut con_header_file = File::options()
.write(true)
.create(true)
.truncate(true)
.open(file_layout.con_header_file_path())
.context("cannot open con header file")?;

con_header_file
Expand Down Expand Up @@ -192,15 +214,3 @@ fn write_part_mht(
mht.write(&mut part_file)?;
Ok(())
}

fn open_file_for_buffered_writing(path: &Path) -> Result<impl Write + Seek, Error> {
let file = File::options().create(true).write(true).open(path)?;
let file = BufWriter::with_capacity(8 * 1024 * 1024, file);
Ok(file)
}

fn open_file_for_buffered_reading(path: &Path) -> Result<impl Read + Seek, Error> {
let file = File::options().read(true).open(path)?;
let file = BufReader::with_capacity(8 * 1024 * 1024, file);
Ok(file)
}
Loading

0 comments on commit e8fff7a

Please sign in to comment.