diff options
-rw-r--r-- | Cargo.lock | 10 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/download.rs | 36 |
3 files changed, 36 insertions, 11 deletions
@@ -486,6 +486,7 @@ dependencies = [ "toml", "ureq", "url", + "uuid", "zip", ] @@ -1214,6 +1215,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom 0.2.3", +] + +[[package]] name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -24,4 +24,5 @@ thiserror = "1.0.30" toml = "0.5.8" ureq = { version = "2.3.0", features = ["json"] } url = { version = "2.2.2", features = ["serde"] } +uuid = { version = "0.8.2", features = ["v4"] } zip = "0.5.13" diff --git a/src/download.rs b/src/download.rs index 75bea57..dfce2fc 100644 --- a/src/download.rs +++ b/src/download.rs @@ -1,10 +1,12 @@ use std::{ + fs, io::{Cursor, Read}, str::FromStr, }; use tempdir::TempDir; use url::Url; +use uuid::Uuid; use zip::ZipArchive; use super::{ @@ -103,18 +105,30 @@ impl Downloader { let data = Cursor::new(data); let mut archive = ZipArchive::new(data)?; - // Here we assume that the zipfile contains only one directory, so we just take the first - // name we find and extract the leading directory name. - let name = archive - .file_names() - .next() - .and_then(|name| name.split('/').next()) - .ok_or(Error::EmptyArchive)? - .to_string(); + let dir = self + .temp_dir + .path() + .join(&Uuid::new_v4().to_hyphenated().to_string()); + fs::create_dir(&dir)?; - archive.extract(self.temp_dir.path())?; + archive.extract(&dir)?; - let extracted_path = self.temp_dir.path().join(name); - minemod::open_mod_or_pack(&extracted_path) + // Some archives contain the mod files directly, so try to open it: + if let Ok(pack) = minemod::open_mod_or_pack(&dir) { + return Ok(pack); + } + + // If the archive does not contain the mod directly, we instead try the subdirectories that + // we've extracted. + for entry in fs::read_dir(&dir)? { + let entry = entry?; + let metadata = fs::metadata(&entry.path())?; + if metadata.is_dir() { + if let Ok(pack) = minemod::open_mod_or_pack(&entry.path()) { + return Ok(pack); + } + } + } + Err(Error::InvalidModDir(dir)) } } |