use std::fs::File;
use std::io::Read;
use log::debug;
use zip::ZipArchive;
use crate::SUPPORTED_ROM_EXTENSIONS;
use crate::error::RomAnalyzerError;
const MAX_ROM_SIZE: u64 = 128 * 1024;
pub fn process_zip_file(
file: File,
original_filename: &str,
) -> Result<(Vec<u8>, String), RomAnalyzerError> {
let mut archive = ZipArchive::new(file)?;
debug!("[+] Analyzing ZIP archive: {}", original_filename);
for i in 0..archive.len() {
let file_in_zip = archive.by_index(i)?;
let entry_name = file_in_zip.name().to_string();
let lower_entry_name = entry_name.to_lowercase();
if file_in_zip.is_dir() {
continue;
}
let is_supported_rom = SUPPORTED_ROM_EXTENSIONS
.iter()
.any(|ext| lower_entry_name.ends_with(ext));
if is_supported_rom {
debug!("[+] Found supported ROM in zip: {}", entry_name);
let mut limited_reader = file_in_zip.take(MAX_ROM_SIZE);
let mut data = Vec::new();
limited_reader.read_to_end(&mut data)?;
return Ok((data, entry_name));
}
}
Err(RomAnalyzerError::ArchiveError(format!(
"No supported ROM files found within the zip archive: {}",
original_filename
)))
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::tempdir;
use zip::write::{FileOptions, ZipWriter};
struct TestZip {
path: String,
_dir: tempfile::TempDir,
}
fn create_zip_file(filename: &str, file_contents: &[u8]) -> Result<TestZip, RomAnalyzerError> {
let dir = tempdir()?;
let zip_path = dir.path().join("test.zip");
let zip_file = File::create(&zip_path)?;
let mut zip = ZipWriter::new(zip_file);
zip.start_file(filename, FileOptions::default())?;
zip.write_all(file_contents)?;
zip.finish()?;
let zip_path_string: String = zip_path
.to_str()
.ok_or_else(|| RomAnalyzerError::new("Path contained invalid UTF-8"))?
.to_string();
Ok(TestZip {
path: zip_path_string,
_dir: dir,
})
}
#[test]
fn test_process_zip_file_no_supported_roms() {
let expected_filename = "unsupported.txt";
let expected_data = b"This is not a ROM.";
let zip_path = create_zip_file(expected_filename, expected_data)
.expect("Failed to create test zip file");
let zip_file = File::open(&zip_path.path).expect("Failed to open zip for reading");
let result = process_zip_file(zip_file, &zip_path.path);
assert!(result.is_err());
let error = result.unwrap_err();
match error {
RomAnalyzerError::ArchiveError(_) => {
assert!(format!("{}", error).starts_with(
"Archive error: No supported ROM files found within the zip archive"
))
}
_ => panic!("Expected ArchiveError variant"),
}
}
#[test]
fn test_process_zip_file_with_supported_rom() {
let expected_filename = "game.nes";
let mut expected_data = vec![0u8; 2000];
expected_data[0..12].copy_from_slice(b"NES ROM DATA");
let zip_path = create_zip_file(expected_filename, &expected_data)
.expect("Failed to create test zip file");
let zip_file = File::open(&zip_path.path).expect("Failed to open zip for reading");
let result = process_zip_file(zip_file, &zip_path.path);
assert!(result.is_ok());
let (extracted_data, extracted_filename) = result.unwrap();
assert_eq!(extracted_data, expected_data);
assert_eq!(extracted_filename, expected_filename);
}
}