pub mod pkg1;
pub mod pkg2;
use crate::util::maple_crypto_constants::{WZ_GMSIV, WZ_MSEAIV};
use crate::util::string_decryptor::{ecb_decryptor::EcbDecryptor, Decryptor};
use crate::{directory::WzDirectoryType, reader, Reader, WzSliceReader};
use std::sync::{Arc, RwLock};
pub fn get_iv_by_maple_version(version: WzMapleVersion) -> [u8; 4] {
match version {
WzMapleVersion::GMS => WZ_GMSIV,
WzMapleVersion::EMS => WZ_MSEAIV,
_ => [0; 4],
}
}
pub fn get_key_by_maple_version(version: WzMapleVersion) -> u32 {
match version {
WzMapleVersion::KMST1198 => 0xDEADBEEF,
_ => 0,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WzMapleVersion {
GMS,
EMS,
BMS,
CLASSIC,
GENERATE,
GETFROMZLZ,
CUSTOM,
UNKNOWN,
KMST1198,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum PKGVersion {
V1,
V2,
#[default]
Unknown,
}
impl From<&str> for PKGVersion {
fn from(value: &str) -> Self {
match value.as_ref() {
"PKG1" => PKGVersion::V1,
"PKG2" => PKGVersion::V2,
_ => PKGVersion::Unknown,
}
}
}
pub fn verify_iv_from_wz_img(buf: &[u8], iv: &[u8; 4]) -> bool {
let keys = Arc::new(RwLock::new(EcbDecryptor::from_iv(*iv))) as Arc<RwLock<dyn Decryptor>>;
let reader = WzSliceReader::new(buf, &keys);
reader.pos.set(1);
reader.read_wz_string().unwrap_or_default() == "Property"
}
pub fn guess_iv_from_wz_img(buf: &[u8]) -> Option<[u8; 4]> {
if buf[0] != 0x73 {
return None;
}
let guess_versions = [
WzMapleVersion::GMS,
WzMapleVersion::EMS,
WzMapleVersion::BMS,
];
for version in guess_versions.iter() {
let iv = get_iv_by_maple_version(*version);
if verify_iv_from_wz_img(buf, &iv) {
return Some(iv);
}
}
None
}
pub fn verify_iv_from_wz_file(buf: &[u8], iv: &[u8; 4]) -> Result<(), reader::Error> {
let keys = Arc::new(RwLock::new(EcbDecryptor::from_iv(*iv))) as Arc<RwLock<dyn Decryptor>>;
let reader = WzSliceReader::new_with_header(buf, &keys);
reader.seek(reader.header.data_start);
let mut entry_count = reader.read_wz_int()?;
if reader.header.ident == PKGVersion::V1 {
if !(0..=1000000).contains(&entry_count) {
return Err(reader::Error::DecryptError(reader.pos.get()));
}
} else if reader.header.ident == PKGVersion::V2 {
let dir_type = WzDirectoryType::from(reader.read_u8_at(reader.pos.get())?);
if !matches!(
dir_type,
WzDirectoryType::WzDirectory | WzDirectoryType::WzImage
) {
return Err(reader::Error::DecryptError(reader.pos.get()));
}
entry_count = 1;
}
if entry_count == 0 {
return Err(reader::Error::DecryptError(reader.pos.get()));
}
let dir_type = WzDirectoryType::from(reader.read_u8()?);
let _wz_name: String;
match dir_type {
WzDirectoryType::MetaAtOffset => {
let str_offset = reader.read_i32()?;
let offset = reader.header.data_start + str_offset as usize;
let meta = reader.read_wz_string_meta_at(offset + 1)?;
_wz_name = reader.try_resolve_wz_string_meta(
&meta.string_type,
meta.offset,
meta.length as usize,
)?;
}
WzDirectoryType::WzDirectory | WzDirectoryType::WzImage => {
let meta = reader.read_wz_string_meta()?;
_wz_name = reader.try_resolve_wz_string_meta(
&meta.string_type,
meta.offset,
meta.length as usize,
)?;
}
_ => return Err(reader::Error::DecryptError(reader.pos.get())),
}
Ok(())
}
pub fn guess_iv_from_wz_file(buf: &[u8]) -> Option<[u8; 4]> {
let guess_versions = [
WzMapleVersion::BMS,
WzMapleVersion::GMS,
WzMapleVersion::EMS,
];
for version in guess_versions.iter() {
let iv = get_iv_by_maple_version(*version);
if verify_iv_from_wz_file(buf, &iv).is_ok() {
return Some(iv);
}
}
None
}