From cd8bf1667494070c3a22ab5d63b559a9742b8a1a Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Sat, 18 Jan 2025 23:47:48 -0600 Subject: more stuff --- src/launcher/assets.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 78 insertions(+), 6 deletions(-) (limited to 'src/launcher/assets.rs') diff --git a/src/launcher/assets.rs b/src/launcher/assets.rs index e540e50..992af2a 100644 --- a/src/launcher/assets.rs +++ b/src/launcher/assets.rs @@ -4,11 +4,13 @@ use std::fmt::{Display, Formatter}; use std::io::ErrorKind; use std::path::{Path, PathBuf}; use std::path::Component::Normal; -use futures::TryStreamExt; +use futures::{stream, TryStreamExt}; use log::{debug, info, warn}; use reqwest::Client; use sha1_smol::Sha1; use tokio::{fs, io}; +use tokio::fs::File; +use tokio_stream::StreamExt; use crate::assets::{Asset, AssetIndex}; use crate::launcher::download::{MultiDownloader, VerifiedDownload}; use crate::util; @@ -33,7 +35,8 @@ pub enum AssetError { DownloadIndex(reqwest::Error), Integrity(IntegrityError), AssetObjectDownload, - AssetVerifyError(FileVerifyError) + AssetVerifyError(FileVerifyError), + AssetNameError(&'static str) } impl Display for AssetError { @@ -48,7 +51,8 @@ impl Display for AssetError { AssetError::DownloadIndex(e) => write!(f, "error downloading asset index: {}", e), AssetError::Integrity(e) => write!(f, "asset index integrity error: {}", e), AssetError::AssetObjectDownload => f.write_str("asset object download failed"), - AssetError::AssetVerifyError(e) => write!(f, "error verifying asset object: {e}") + AssetError::AssetVerifyError(e) => write!(f, "error verifying asset object: {e}"), + AssetError::AssetNameError(e) => write!(f, "invalid asset name: {e}") } } } @@ -105,7 +109,7 @@ impl AssetRepository { } pub async fn load_index(&self, index: &DownloadInfo, id: Option<&str>) -> Result { - let Some(id) = index.id.as_ref().map(|s| s.as_str()).or(id) else { + let Some(id) = id else { return Err(AssetError::InvalidId(None)); }; @@ -188,6 +192,11 @@ impl AssetRepository { format!("{}{:02x}/{}", super::constants::URL_RESOURCE_BASE, obj.hash.bytes()[0], obj.hash) } + fn get_object_path(&self, obj: &Asset) -> PathBuf { + let hex_digest = obj.hash.to_string(); + [self.home.as_ref(), OsStr::new(OBJECT_PATH), OsStr::new(&hex_digest[..2]), OsStr::new(&hex_digest)].iter().collect() + } + async fn ensure_dir(path: impl AsRef) -> Result<(), io::Error> { match fs::create_dir(path).await { Ok(_) => Ok(()), @@ -206,8 +215,7 @@ impl AssetRepository { })?; for object in index.objects.iter() { - let hex_digest = object.hash.to_string(); - let path = [objects_path.as_ref(), OsStr::new(&hex_digest[..2]), OsStr::new(&hex_digest)].iter().collect::(); + let path = self.get_object_path(object); Self::ensure_dir(path.parent().unwrap()).await.map_err(|error| AssetError::IO { error, what: "creating directory for object" })?; @@ -229,6 +237,70 @@ impl AssetRepository { Ok(()) } + + pub async fn reconstruct_assets(&self, index: &AssetIndex, instance_path: &Path, index_id: Option<&str>) -> Result, AssetError> { + let target_path: PathBuf; + let Some(index_id) = index_id else { + return Err(AssetError::InvalidId(None)); + }; + + if index.virtual_assets { + target_path = [self.home.as_ref(), OsStr::new("virtual"), OsStr::new(index_id)].iter().collect(); + } else if index.map_to_resources { + target_path = [instance_path, Path::new("resources")].iter().collect(); + } else { + info!("This asset index does not request a virtual assets folder. Nothing to be done."); + return Ok(None); + } + + info!("Reconstructing virtual assets for {}", index_id); + + fs::create_dir_all(&target_path).await.map_err(|e| AssetError::from(("creating virtual assets directory", e)))?; + + stream::iter(index.objects.iter() + .map(|object| { + let obj_path = util::check_path(object.name.as_str()).map_err(AssetError::AssetNameError)?; + let obj_path = target_path.join(obj_path); + + Ok((object, obj_path)) + })) + .try_filter_map(|(object, obj_path)| async move { + match util::verify_file(&obj_path, Some(object.size), Some(object.hash)).await { + Ok(_) => { + debug!("Not copying asset {}, integrity matches.", object.name); + Ok(None) + } + Err(FileVerifyError::Open(_, e)) if e.kind() == ErrorKind::NotFound => { + debug!("Copying asset {}, file does not exist.", object.name); + Ok(Some((object, obj_path))) + }, + Err(FileVerifyError::Integrity(_, e)) => { + debug!("Copying asset {}: {}", object.name, e); + Ok(Some((object, obj_path))) + }, + Err(e) => { + debug!("Error while reconstructing assets: {e}"); + Err(AssetError::AssetVerifyError(e)) + } + } + }) + .try_for_each_concurrent(16, |(object, obj_path)| async move { + if let Some(parent) = obj_path.parent() { + fs::create_dir_all(parent).await + .inspect_err(|e| debug!("Error creating directory for asset object {}: {e}", object.name)) + .map_err(|e| AssetError::from(("creating asset object directory", e)))?; + } + + let mut fromfile = File::open(self.get_object_path(object)).await + .map_err(|e| AssetError::from(("opening source object", e)))?; + let mut tofile = File::create(&obj_path).await + .map_err(|e| AssetError::from(("creating target object", e)))?; + + io::copy(&mut fromfile, &mut tofile).await.map_err(|e| AssetError::from(("copying asset object", e)))?; + debug!("Copied object {} to {}.", object.name, obj_path.display()); + Ok(()) + }).await.map(|_| Some(target_path)) + } } mod tests { -- cgit v1.2.3-70-g09d2