use std::{fs, io, path::Path}; use log::info; use zip::ZipArchive; use crate::{ errors::McError, minecraft::manifests::{Library, Version}, }; pub fn extract_natives( cfg: &crate::config::Config, version: &Version, ) -> Result<(), McError> { let natives_dir = cfg .data_dir .join("minecraft") .join("versions") .join(&version.id) .join("natives"); info!("Extracting natives for {} into {:?}", version.id, natives_dir); if natives_dir.exists() { fs::remove_dir_all(&natives_dir)?; } fs::create_dir_all(&natives_dir)?; for lib in &version.libraries { if !library_allowed(lib) { continue; } let natives = match &lib.natives { | Some(n) => n, | None => continue, }; let classifier = match natives.get("linux") { | Some(c) => c, | None => continue, }; let classifiers = match &lib.downloads.classifiers { | Some(c) => c, | None => continue, }; let artifact = match classifiers.get(classifier) { | Some(a) => a, | None => continue, }; let jar_path = cfg .data_dir .join("minecraft") .join("libraries") .join(&artifact.path); info!("Extracting natives from {:?}", jar_path); extract_zip(&jar_path, &natives_dir)?; } Ok(()) } fn library_allowed(lib: &Library) -> bool { let rules = match &lib.rules { | Some(r) => r, | None => return true, }; let mut allowed = false; for rule in rules { let os_match = match &rule.os { | Some(os) => os.name == "linux", | None => true, }; if os_match { allowed = rule.action == "allow"; } } allowed } fn extract_zip(jar_path: &Path, out_dir: &Path) -> Result<(), McError> { let file = fs::File::open(jar_path)?; let mut zip = ZipArchive::new(file)?; for i in 0..zip.len() { let mut entry = zip.by_index(i)?; let name = entry.name(); if name.starts_with("META-INF/") { continue; } let out_path = out_dir.join(name); if entry.is_dir() { fs::create_dir_all(&out_path)?; continue; } if let Some(parent) = out_path.parent() { fs::create_dir_all(parent)?; } let mut out_file = fs::File::create(&out_path)?; io::copy(&mut entry, &mut out_file)?; } Ok(()) }