From be5879eaaa4000891b1e5369250fec48433b806b Mon Sep 17 00:00:00 2001 From: Daniel Schadt Date: Sun, 7 Nov 2021 00:36:29 +0100 Subject: make downloads work with more archives Some mods do not come bundled in a separate directory, so we also support archives that do not contain an inner directory now (e.g. the "charcoal" mod). --- Cargo.lock | 10 ++++++++++ Cargo.toml | 1 + src/download.rs | 36 +++++++++++++++++++++++++----------- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bce8b00..fb5d7c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -486,6 +486,7 @@ dependencies = [ "toml", "ureq", "url", + "uuid", "zip", ] @@ -1213,6 +1214,15 @@ version = "0.7.6" 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" diff --git a/Cargo.toml b/Cargo.toml index 5bc82df..988af09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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)) } } -- cgit v1.2.3