use std::collections::HashMap;
use std::fs;
use std::fs::File;
use std::io::{Cursor, Read, Write};
use std::path::Path;
use zip::ZipArchive;
use crate::error::Result;
use crate::sprites::SpriteManager;
use image::ImageFormat;
pub type WszArchive = HashMap<String, Vec<u8>>;
pub fn unpack_wsz<P: AsRef<Path>>(path: P) -> Result<WszArchive> {
let file = File::open(path)?;
let mut archive = ZipArchive::new(file)?;
let mut contents = HashMap::new();
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let name = file.name().to_string();
if file.is_dir() {
continue;
}
let mut data = Vec::new();
file.read_to_end(&mut data)?;
contents.insert(name, data);
}
Ok(contents)
}
pub fn unpack_wsz_bytes(data: &[u8]) -> Result<WszArchive> {
let cursor = Cursor::new(data);
let mut archive = ZipArchive::new(cursor)?;
let mut contents = HashMap::new();
for i in 0..archive.len() {
let mut file = archive.by_index(i)?;
let name = file.name().to_string();
if file.is_dir() {
continue;
}
let mut data = Vec::new();
file.read_to_end(&mut data)?;
contents.insert(name, data);
}
Ok(contents)
}
pub fn pack_wsz_dir<P: AsRef<Path>>(dir_path: P, output_path: P) -> Result<()> {
let dir_path = dir_path.as_ref();
let output_path = output_path.as_ref();
let file = File::create(output_path)?;
let mut zip = zip::ZipWriter::new(file);
let options = zip::write::SimpleFileOptions::default()
.compression_method(zip::CompressionMethod::Deflated)
.unix_permissions(0o644);
let mut added_files = std::collections::HashSet::new();
let mut entries = fs::read_dir(dir_path)?.filter_map(|e| e.ok()).collect::<Vec<_>>();
entries.sort_by(|a, b| a.path().cmp(&b.path()));
let mut sprites_by_sheet = HashMap::new();
let sprite_manager = SpriteManager::new();
let all_sprite_defs = sprite_manager.get_sprite_definitions();
let all_sprite_sheets = SpriteManager::sprite_sheet_names();
for entry in &entries {
let path = entry.path();
if path.is_dir() {
let bmp_name = path
.with_extension("BMP")
.file_name()
.and_then(|s| s.to_str())
.unwrap_or_default()
.to_uppercase();
if !all_sprite_sheets.contains(&bmp_name) {
continue;
}
let sprite_defs = all_sprite_defs
.values()
.filter(|def| def.sprite_sheet == bmp_name)
.collect::<Vec<_>>();
if sprite_defs.is_empty() {
continue;
}
let mut sprite_images = HashMap::new();
let subdir_entries = fs::read_dir(&path)?.filter_map(|e| e.ok()).collect::<Vec<_>>();
for subentry in subdir_entries {
let sprite_path = subentry.path();
if sprite_path.is_file() && sprite_path.extension().map_or(false, |ext| ext == "png") {
let sprite_name = sprite_path
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or_default()
.to_string()
.to_uppercase();
if !sprite_defs.iter().any(|def| def.name == sprite_name) {
continue;
}
match image::open(&sprite_path) {
Ok(img) => {
let rgba_img = img.to_rgba8();
sprite_images.insert(sprite_name, rgba_img);
}
Err(e) => {
eprintln!("Error loading sprite {}: {}", sprite_path.display(), e);
}
}
}
}
sprites_by_sheet.insert(bmp_name, sprite_images);
}
}
for (sheet_name, sprite_images) in sprites_by_sheet {
match sprite_manager.construct_sprite_sheet(&sprite_images, &sheet_name) {
Ok(sprite_sheet) => {
let mut bmp_data = Vec::new();
let mut cursor = Cursor::new(&mut bmp_data);
sprite_sheet.write_to(&mut cursor, ImageFormat::Bmp)?;
let file_name = format!("{}", sheet_name);
zip.start_file(&file_name, options)?;
zip.write_all(&bmp_data)?;
added_files.insert(file_name);
}
Err(e) => {
eprintln!("Error reconstructing {}.BMP: {}", sheet_name, e);
}
}
}
for entry in entries {
let path = entry.path();
if path.is_file() {
let file_name = path
.file_name()
.and_then(|s| s.to_str())
.unwrap_or_default()
.to_string();
if added_files.contains(&file_name) {
continue;
}
let file_data = fs::read(&path)?;
zip.start_file(&file_name, options)?;
zip.write_all(&file_data)?;
added_files.insert(file_name);
}
}
zip.finish()?;
Ok(())
}