use serde::{Deserialize, Serialize};
use std::{ffi::OsStr, fs::File, io::Read, path::PathBuf};
const ALLOWED_GAMES: [&str; 2] = ["EM1", "EM2"];
const ALLOWED_PLATFORMS: [&str; 2] = ["wii", "pc"];
const BANNED_EXTENSIONS: [&str; 6] = ["dll", "so", "exe", "sh", "bat", "scr"];
pub fn validate(path: &PathBuf) -> Result<ModInfo, Box<dyn std::error::Error>> {
let mut mod_info_path = path.clone();
mod_info_path.push("mod.json");
let mut mod_description_path = path.clone();
mod_description_path.push("description.md");
if !mod_info_path.exists() {
return Err("mod.json does not exist.".into());
}
let mut mod_info_file = File::open(mod_info_path)?;
let mut mod_info_buffer = String::new();
mod_info_file.read_to_string(&mut mod_info_buffer)?;
let mut mod_info: ModInfo = serde_json::from_str(&mod_info_buffer)?;
if mod_info.name.trim().is_empty() {
return Err("mod name is empty.".into());
}
if mod_info.description.trim().is_empty() {
if mod_description_path.exists() {
let mut mod_description_file = File::open(mod_description_path)?;
let mut mod_description = String::new();
mod_description_file.read_to_string(&mut mod_description)?;
if mod_description.trim().is_empty() {
return Err("mod description is empty.".into());
}
mod_info.description = mod_description;
}
}
if !ALLOWED_GAMES.contains(&mod_info.game.as_str()) {
return Err("could not recognize defined game.".into());
}
if !ALLOWED_PLATFORMS.contains(&mod_info.platform.as_str()) {
return Err("could not recognize defined platform.".into());
}
if mod_info.custom_game_files_path.trim().is_empty() {
return Err("custom game files path is empty.".into());
}
if mod_info.custom_textures_path.trim().is_empty() {
return Err("custom textures path is empty.".into());
}
if PathBuf::from(&mod_info.custom_textures_path).is_absolute()
|| PathBuf::from(&mod_info.custom_game_files_path).is_absolute()
{
return Err("you are not allowed to have absolute paths on custom file path.".into());
}
if PathBuf::from(&mod_info.custom_textures_path).exists() {
return Err("custom textures path does not exist.".into());
}
if PathBuf::from(&mod_info.custom_game_files_path).exists() {
return Err("custom game files path does not exist.".into());
}
if mod_info.icon_path.trim().is_empty() {
return Err("mod icon path is empty.".into());
}
if PathBuf::from(&mod_info.icon_path).is_absolute() {
return Err("you are not allowed to have absolute paths on mod icon.".into());
}
if PathBuf::from(&mod_info.icon_path).exists() {
return Err("mod icon does not exist.".into());
}
for entry in walkdir::WalkDir::new(path).into_iter() {
let res = entry?;
if res.path().is_dir() {
continue;
}
let extension = match res.path().extension() {
Some(s) => s,
None => OsStr::new(""),
};
if !extension.is_empty() {
let formatted_extension = extension.to_str().unwrap().to_string().to_lowercase();
if BANNED_EXTENSIONS.contains(&formatted_extension.as_str()) {
return Err(format!("mod contains illegal file ({})", formatted_extension).into());
}
}
}
Ok(mod_info)
}
#[derive(Serialize, Deserialize)]
pub struct ModInfo {
pub name: String,
pub game: String,
pub platform: String,
pub description: String,
pub dependencies: Vec<String>,
pub custom_textures_path: String,
pub custom_game_files_path: String,
pub icon_path: String,
}