diff --git a/src/main.rs b/src/main.rs index 8e0555d..84d62d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,7 @@ async fn main()->AResult<()>{ let cli=Cli::parse(); match cli.command{ Commands::Roblox(commands)=>commands.run().await, - Commands::Source(commands)=>commands.run(), + Commands::Source(commands)=>commands.run().await, Commands::ConvertTextures=>common::convert_textures(), } } diff --git a/src/source.rs b/src/source.rs index 9d60565..95cad8f 100644 --- a/src/source.rs +++ b/src/source.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::path::{Path,PathBuf}; use clap::{Args,Subcommand}; use anyhow::Result as AResult; use strafesnet_deferred_loader::deferred_loader::LoadFailureMode; @@ -37,10 +37,10 @@ pub struct BSPContentsSubcommand { } impl Commands{ - pub fn run(self)->AResult<()>{ + pub async fn run(self)->AResult<()>{ match self{ - Commands::SourceToSNF(subcommand)=>source_to_snf(subcommand.input_files,subcommand.output_folder), Commands::ExtractTextures(subcommand)=>extract_textures(vec![subcommand.bsp_file],subcommand.vpk_dir_files), + Commands::SourceToSNF(subcommand)=>source_to_snf(subcommand.input_files,subcommand.output_folder).await, Commands::VPKContents(subcommand)=>vpk_contents(subcommand.input_file), Commands::BSPContents(subcommand)=>bsp_contents(subcommand.input_file), } @@ -288,47 +288,45 @@ impl std::fmt::Display for ConvertError{ } impl std::error::Error for ConvertError{} -type MapThread=std::thread::JoinHandle<Result<(),ConvertError>>; +async fn convert_to_snf(path:&Path,output_folder:PathBuf)->AResult<()>{ + let entire_file=tokio::fs::read(path).await?; -fn source_to_snf(pathlist:Vec<std::path::PathBuf>,output_folder:PathBuf)->AResult<()>{ - let n_paths=pathlist.len(); - let start = std::time::Instant::now(); - let mut threads:std::collections::VecDeque<MapThread>=std::collections::VecDeque::new(); - let mut i=0; - let mut join_thread=|thread:MapThread|{ - i+=1; - if let Err(e)=thread.join(){ - println!("thread error: {:?}",e); - }else{ - println!("{}/{}",i,n_paths); - } - }; - for path in pathlist{ - if 32<=threads.len(){ - join_thread(threads.pop_front().unwrap()); - } - let output_folder=output_folder.clone(); - threads.push_back(std::thread::spawn(move ||{ - let bsp=strafesnet_bsp_loader::read( - std::fs::File::open(path.as_path()) - .map_err(ConvertError::IO)? - ).map_err(ConvertError::BspRead)?; + let bsp=strafesnet_bsp_loader::read( + std::io::Cursor::new(entire_file) + ).map_err(ConvertError::BspRead)?; - let map=bsp.to_snf(LoadFailureMode::DefaultToNone).map_err(ConvertError::BspLoad)?; + let map=bsp.to_snf(LoadFailureMode::DefaultToNone).map_err(ConvertError::BspLoad)?; - let mut dest=output_folder.clone(); - dest.push(path.file_stem().unwrap()); - dest.set_extension("snfm"); - let file=std::fs::File::create(dest).map_err(ConvertError::IO)?; + let mut dest=output_folder; + dest.push(path.file_stem().unwrap()); + dest.set_extension("snfm"); + let file=std::fs::File::create(dest).map_err(ConvertError::IO)?; - strafesnet_snf::map::write_map(file,map).map_err(ConvertError::SNFMap)?; - Ok(()) - })); - } + strafesnet_snf::map::write_map(file,map).map_err(ConvertError::SNFMap)?; - for thread in threads{ - join_thread(thread); - } - println!("{:?}", start.elapsed()); + Ok(()) +} +async fn source_to_snf(paths:Vec<std::path::PathBuf>,output_folder:PathBuf)->AResult<()>{ + let start=std::time::Instant::now(); + + let thread_limit=std::thread::available_parallelism()?.get(); + let mut it=paths.into_iter(); + static SEM:tokio::sync::Semaphore=tokio::sync::Semaphore::const_new(0); + SEM.add_permits(thread_limit); + + while let (Ok(permit),Some(path))=(SEM.acquire().await,it.next()){ + let output_folder=output_folder.clone(); + tokio::spawn(async move{ + let result=convert_to_snf(path.as_path(),output_folder).await; + drop(permit); + match result{ + Ok(())=>(), + Err(e)=>println!("Convert error: {e:?}"), + } + }); + } + _=SEM.acquire_many(thread_limit as u32).await.unwrap(); + + println!("elapsed={:?}", start.elapsed()); Ok(()) }