From cfc87ea505a330f727440b12c6b882de03df5baf Mon Sep 17 00:00:00 2001 From: bigfoot547 Date: Wed, 15 Jan 2025 19:45:33 -0600 Subject: wip: asset index --- src/launcher/assets.rs | 114 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/launcher/assets.rs (limited to 'src/launcher/assets.rs') diff --git a/src/launcher/assets.rs b/src/launcher/assets.rs new file mode 100644 index 0000000..020885f --- /dev/null +++ b/src/launcher/assets.rs @@ -0,0 +1,114 @@ +use std::error::Error; +use std::fmt::{Display, Formatter, Write}; +use std::io::ErrorKind; +use std::path::{Path, PathBuf}; +use std::path::Component::Normal; +use log::{debug, info, warn}; +use tokio::{fs, io}; +use crate::assets::AssetIndex; +use crate::util; +use crate::util::FileVerifyError; +use crate::version::DownloadInfo; + +const INDEX_PATH: &'static str = "indexes"; +const OBJECT_PATH: &'static str = "objects"; + +pub struct AssetRepository { + online: bool, + home: PathBuf +} + +#[derive(Debug)] +pub enum AssetError { + InvalidId(Option), + IO { what: &'static str, error: io::Error } +} + +impl Display for AssetError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + AssetError::InvalidId(None) => f.write_str("missing asset index id"), + AssetError::InvalidId(Some(id)) => write!(f, "invalid asset index id: {}", id), + AssetError::IO { what, error } => write!(f, "i/o error ({}): {}", what, error) + } + } +} + +impl Error for AssetError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + AssetError::IO { error, .. } => Some(error), + _ => None + } + } +} + +impl From<(&'static str, io::Error)> for AssetError { + fn from((what, error): (&'static str, io::Error)) -> Self { + AssetError::IO { what, error } + } +} + +impl AssetRepository { + pub async fn new(online: bool, home: impl AsRef) -> Result { + let home = home.as_ref().to_owned(); + + match fs::create_dir_all(&home).await { + Ok(_) => (), + Err(e) => match e.kind() { + ErrorKind::AlreadyExists => (), + _ => return Err(e) + } + }; + + Ok(AssetRepository { + online, + home + }) + } + + fn get_index_path(&self, id: &str) -> Result { + let indexes_path: &Path = (&self.home, INDEX_PATH).as_ref(); + let Some(Normal(path)) = Path::new(id).components().last() else { + return Err(AssetError::InvalidId(id.into())); + }; + + let path = path.to_str().ok_or(AssetError::InvalidId(Some(path.to_string_lossy())))?; + + // FIXME: change this once "add_extension" is stabilized + Ok((indexes_path, format!("{}.json", path)).into()) + } + + 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 { + return Err(AssetError::InvalidId(None)); + }; + + info!("Loading asset index {}", id); + + let path = self.get_index_path(id)?; + debug!("Asset index {} is located at {}", id, path); + + match util::verify_file(&path, index.size, index.sha1).await { + Ok(_) => todo!(), // load local index + Err(FileVerifyError::Open(_, e)) => match e.kind() { + ErrorKind::NotFound => (), // download + _ => return Err(("opening asset index", e).into()) + }, + Err(FileVerifyError::Integrity(_, e)) => { + info!("Asset index {} has mismatched integrity: {}, must download it.", id, e); + let _ = fs::remove_file(&path).await.map_err(|e| warn!("Error deleting modified index {}: {} (ignored)", id, e)); + // download + }, + Err(FileVerifyError::Read(_, e)) => return Err(("reading asset index", e).into()) + } + + if !self.online { + warn!("Must redownload asset index {}, but the launcher is in offline mode. Please try again in online mode.", id); + return todo!(); + } + + todo!() + } +} + -- cgit v1.2.3-70-g09d2