use std::{fs, io, path::Path};
use crate::emu::Emu;
use crate::maps::mem64::Permission;
impl Emu {
pub fn set_maps_folder(&mut self, folder: &str) {
self.cfg.maps_folder = folder.to_string();
if self.maps_folder_is_valid(folder) {
return;
}
let is_32bit = folder.contains("windows/x86") && !folder.contains("x86_64");
if is_32bit {
log::trace!(
"Maps folder '{}' incomplete, attempting legacy download...",
folder
);
if let Err(e) = self.download_and_extract_maps(folder) {
log::error!("Failed to download 32-bit maps '{}': {}", folder, e);
{
log::warn!(
"Cannot obtain 32-bit Windows maps. Supply your own with `--maps <dir>`."
);
return;
}
}
return;
}
log::trace!(
"Maps folder '{}' empty; DLLs will be auto-fetched from the symbol server on demand",
folder
);
let _ = std::fs::create_dir_all(folder);
}
fn maps_folder_is_valid(&self, folder: &str) -> bool {
let folder_path = Path::new(folder);
if !folder_path.exists() {
println!("folder doesnt exist");
return false;
}
let essential_files = if !self.cfg.arch.is_64bits() {
println!("self.cfg.emulate_winapi: {}", self.cfg.emulate_winapi);
if self.cfg.skip_unimplemented {
vec!["ntdll.dll", "kernel32.dll", "banzai.csv"]
} else {
vec!["ntdll.dll", "kernel32.dll"]
}
} else {
vec!["ntdll.dll", "kernel32.dll"]
};
for file in essential_files {
let file_path = folder_path.join(file);
if !file_path.exists() {
println!("essential file missing: {}", file_path.display());
log::trace!(
"Essential file '{}' missing from maps folder",
file_path.display()
);
return false;
}
}
true
}
fn download_and_extract_maps(&self, folder: &str) -> Result<(), Box<dyn std::error::Error>> {
let url = match folder {
f if f.contains("windows/x86") && !f.contains("x86_64") => {
"https://github.com/sha0coder/mwemu/releases/download/maps/maps32.zip"
}
_ => return Err(format!("Unknown maps folder: {}", folder).into()),
};
log::trace!(
"Downloading {} from GitHub releases... (this may take a moment)",
folder
);
let response = ureq::get(url)
.timeout(std::time::Duration::from_secs(30))
.call()?;
if response.status() != 200 {
return Err(format!("Failed to download: HTTP {}", response.status()).into());
}
let mut bytes = Vec::new();
response.into_reader().read_to_end(&mut bytes)?;
log::trace!("Downloaded {} MB", bytes.len() / 1024 / 1024);
let cursor = std::io::Cursor::new(bytes);
let mut archive = zip::ZipArchive::new(cursor)?;
log::trace!("Extracting {} files...", archive.len());
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let outpath = Path::new(file.name());
if file.name().ends_with('/') {
fs::create_dir_all(outpath)?;
} else {
if let Some(p) = outpath.parent() {
if !p.exists() {
fs::create_dir_all(p)?;
}
}
let mut outfile = fs::File::create(outpath)?;
io::copy(&mut file, &mut outfile)?;
}
}
log::trace!("Successfully extracted maps folder: {}", folder);
Ok(())
}
pub fn get_base_addr(&self) -> Option<u64> {
let map = self.maps.get_map_by_name("code")?;
Some(map.get_base())
}
pub fn filename_to_mapname(&self, filename: &str) -> String {
std::path::Path::new(filename)
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or(filename)
.to_string()
}
pub fn free(&mut self, name: &str) {
self.maps.free(name);
}
pub fn alloc(&mut self, name: &str, size: u64, permission: Permission) -> u64 {
let addr = match self.maps.alloc(size) {
Some(a) => a,
None => {
log::trace!("low memory");
return 0;
}
};
self.maps
.create_map(name, addr, size, permission)
.expect("cannot create map from alloc api");
addr
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn maps_folder_requires_banzai_csv_only_when_skip_unimplemented() {
let dir = tempfile::tempdir().expect("temp maps dir");
fs::write(dir.path().join("ntdll.dll"), []).expect("ntdll fixture");
fs::write(dir.path().join("kernel32.dll"), []).expect("kernel32 fixture");
let folder = dir.path().to_str().expect("utf8 temp path");
let mut emu = crate::emu32();
emu.cfg.skip_unimplemented = false;
assert!(emu.maps_folder_is_valid(folder));
emu.cfg.skip_unimplemented = true;
assert!(!emu.maps_folder_is_valid(folder));
fs::write(dir.path().join("banzai.csv"), []).expect("banzai fixture");
assert!(emu.maps_folder_is_valid(folder));
}
}