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(())
 }