You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

307 lines
10 KiB

use serde::{Serialize, Deserialize};
// TODO: actually use this
#[derive(Debug)]
pub enum ItemParseError {
InvalidESWeaponBytes,
InvalidESWeaponType,
InvalidESWeaponGrind,
InvalidESWeaponSpecial,
InvalidESWeaponName,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, strum_macros::EnumIter)]
pub enum ESWeaponType {
Saber = 0,
Sword,
Blade,
Partisan,
Slicer,
Gun,
Rifle,
Mechgun,
Shot,
Can,
Rod,
Wand,
Twin,
Claw,
Bazooka,
Needle,
Scythe,
Hammer,
Moon,
Psychogun,
Punch,
Windmill,
Harisen,
Katana,
JCutter,
Swords = 0x35,
Launcher,
Cards,
Knuckle,
Axe,
}
impl ESWeaponType {
pub fn to_value(&self) -> u8 {
*self as u8
}
pub fn from_value(value: u8) -> Result<ESWeaponType, ItemParseError> {
match value {
0 => Ok(ESWeaponType::Saber),
1 => Ok(ESWeaponType::Sword),
2 => Ok(ESWeaponType::Blade),
3 => Ok(ESWeaponType::Partisan),
4 => Ok(ESWeaponType::Slicer),
5 => Ok(ESWeaponType::Gun),
6 => Ok(ESWeaponType::Rifle),
7 => Ok(ESWeaponType::Mechgun),
8 => Ok(ESWeaponType::Shot),
9 => Ok(ESWeaponType::Can),
10 => Ok(ESWeaponType::Rod),
11 => Ok(ESWeaponType::Wand),
12 => Ok(ESWeaponType::Twin),
13 => Ok(ESWeaponType::Claw),
14 => Ok(ESWeaponType::Bazooka),
15 => Ok(ESWeaponType::Needle),
16 => Ok(ESWeaponType::Scythe),
17 => Ok(ESWeaponType::Hammer),
18 => Ok(ESWeaponType::Moon),
19 => Ok(ESWeaponType::Psychogun),
20 => Ok(ESWeaponType::Punch),
21 => Ok(ESWeaponType::Windmill),
22 => Ok(ESWeaponType::Harisen),
23 => Ok(ESWeaponType::Katana),
24 => Ok(ESWeaponType::JCutter),
53 => Ok(ESWeaponType::Swords),
54 => Ok(ESWeaponType::Launcher),
55 => Ok(ESWeaponType::Cards),
56 => Ok(ESWeaponType::Knuckle),
57 => Ok(ESWeaponType::Axe),
_ => Err(ItemParseError::InvalidESWeaponType),
}
}
pub fn parse_type(data: [u8; 3]) -> Result<ESWeaponType, ItemParseError> {
match data {
[0x00, 0x70, 0x00] => Ok(ESWeaponType::Saber),
[0x00, 0x71, 0x00] => Ok(ESWeaponType::Sword),
[0x00, 0x72, 0x00] => Ok(ESWeaponType::Blade),
[0x00, 0x73, 0x00] => Ok(ESWeaponType::Partisan),
[0x00, 0x74, 0x00] => Ok(ESWeaponType::Slicer),
[0x00, 0x75, 0x00] => Ok(ESWeaponType::Gun),
[0x00, 0x76, 0x00] => Ok(ESWeaponType::Rifle),
[0x00, 0x77, 0x00] => Ok(ESWeaponType::Mechgun),
[0x00, 0x78, 0x00] => Ok(ESWeaponType::Shot),
[0x00, 0x79, 0x00] => Ok(ESWeaponType::Can),
[0x00, 0x7A, 0x00] => Ok(ESWeaponType::Rod),
[0x00, 0x7B, 0x00] => Ok(ESWeaponType::Wand),
[0x00, 0x7C, 0x00] => Ok(ESWeaponType::Twin),
[0x00, 0x7D, 0x00] => Ok(ESWeaponType::Claw),
[0x00, 0x7E, 0x00] => Ok(ESWeaponType::Bazooka),
[0x00, 0x7F, 0x00] => Ok(ESWeaponType::Needle),
[0x00, 0x80, 0x00] => Ok(ESWeaponType::Scythe),
[0x00, 0x81, 0x00] => Ok(ESWeaponType::Hammer),
[0x00, 0x82, 0x00] => Ok(ESWeaponType::Moon),
[0x00, 0x83, 0x00] => Ok(ESWeaponType::Psychogun),
[0x00, 0x84, 0x00] => Ok(ESWeaponType::Punch),
[0x00, 0x85, 0x00] => Ok(ESWeaponType::Windmill),
[0x00, 0x86, 0x00] => Ok(ESWeaponType::Harisen),
[0x00, 0x87, 0x00] => Ok(ESWeaponType::Katana),
[0x00, 0x88, 0x00] => Ok(ESWeaponType::JCutter),
[0x00, 0xA5, 0x00] => Ok(ESWeaponType::Swords),
[0x00, 0xA6, 0x00] => Ok(ESWeaponType::Launcher),
[0x00, 0xA7, 0x00] => Ok(ESWeaponType::Cards),
[0x00, 0xA8, 0x00] => Ok(ESWeaponType::Knuckle),
[0x00, 0xA9, 0x00] => Ok(ESWeaponType::Axe),
_ => Err(ItemParseError::InvalidESWeaponBytes),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, strum_macros::EnumIter)]
pub enum ESWeaponSpecial {
Jellen = 1,
Zalure,
HPRegen,
TPRegen,
Burning,
Tempest,
Blizzard,
Arrest,
Chaos,
Hell,
Spirit,
Berserk,
Demons,
Gush,
Geist,
Kings,
}
impl ESWeaponSpecial {
pub fn to_value(&self) -> u8 {
*self as u8
}
pub fn from_value(value: u8) -> Result<ESWeaponSpecial, ItemParseError> {
match value{
1 => Ok(ESWeaponSpecial::Jellen),
2 => Ok(ESWeaponSpecial::Zalure),
3 => Ok(ESWeaponSpecial::HPRegen),
4 => Ok(ESWeaponSpecial::TPRegen),
5 => Ok(ESWeaponSpecial::Burning),
6 => Ok(ESWeaponSpecial::Tempest),
7 => Ok(ESWeaponSpecial::Blizzard),
8 => Ok(ESWeaponSpecial::Arrest),
9 => Ok(ESWeaponSpecial::Chaos),
10 => Ok(ESWeaponSpecial::Hell),
11 => Ok(ESWeaponSpecial::Spirit),
12 => Ok(ESWeaponSpecial::Berserk),
13 => Ok(ESWeaponSpecial::Demons),
14 => Ok(ESWeaponSpecial::Gush),
15 => Ok(ESWeaponSpecial::Geist),
16 => Ok(ESWeaponSpecial::Kings),
_ => Err(ItemParseError::InvalidESWeaponSpecial),
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ESWeapon {
pub esweapon: ESWeaponType,
pub special: Option<ESWeaponSpecial>,
pub name: String,
pub grind: u8,
}
impl ESWeapon {
pub fn new(t: ESWeaponType) -> ESWeapon {
ESWeapon {
esweapon: t,
special: None,
name: "".to_owned(),
grind: 0,
}
}
pub fn bytes_from_name(&self) -> [u8; 6] {
let mut result = [0u16; 3];
let mut letters = [0u8; 8];
letters[0..self.name.len()].clone_from_slice(&self.name.to_uppercase().into_bytes());
for letter in letters.iter_mut() {
*letter &= 0x3F;
}
result[0] = 0x8000 + (0x20 * letters[0] as u16) + (letters[1] as u16);
result[1] = 0x8000 + (0x400 * letters[2] as u16) + (0x20 * letters[3] as u16) + (letters[4] as u16);
result[2] = 0x8000 + (0x400 * letters[5] as u16) + (0x20 * letters[6] as u16) + (letters[7] as u16);
[result[0].to_be_bytes()[0], result[0].to_be_bytes()[1],
result[1].to_be_bytes()[0], result[1].to_be_bytes()[1],
result[2].to_be_bytes()[0], result[2].to_be_bytes()[1]]
}
// TODO: error handling, ensure name is never more than 8
pub fn name_from_bytes(namebytes: &[u8]) -> String {
let buf: [u16; 3] = [
u16::from_be_bytes([namebytes[0], namebytes[1]]),
u16::from_be_bytes([namebytes[2], namebytes[3]]),
u16::from_be_bytes([namebytes[4], namebytes[5]]),
];
let mut name: Vec<u8> = vec![
((buf[0] - 0x8000) / 0x20 + 0x40) as u8,
((buf[0] - 0x8000) % 0x20 + 0x40) as u8,
((buf[1] - 0x8000) / 0x400 + 0x40) as u8,
(((buf[1] - 0x8000) % 0x400) / 0x20 + 0x40) as u8,
(((buf[1] - 0x8000) % 0x400) % 0x20 + 0x40) as u8,
((buf[2] - 0x8000) / 0x400 + 0x40) as u8,
(((buf[2] - 0x8000) % 0x400) / 0x20 + 0x40) as u8,
(((buf[2] - 0x8000) % 0x400) % 0x20 + 0x40) as u8,
];
name.retain(|&x| x > 0x40 && x < 0x5B);
String::from_utf8(name).unwrap()
}
pub fn as_bytes(&self) -> [u8; 16] {
let mut result = [0u8; 16];
result[1] = 0x70 + self.esweapon.to_value();
result[2] = self.special.map(|s| s.to_value()).unwrap_or(0);
result[3] = self.grind;
//result[4] = tekked/untekked flag
result[6..12].clone_from_slice(&self.bytes_from_name());
result
}
// TODO: return Result<ESWeapon, ESWeaponError>
pub fn from_bytes(bytes: [u8; 16]) -> ESWeapon {
let esweapon = ESWeaponType::from_value(bytes[1] - 0x70).unwrap();
let special = ESWeaponSpecial::from_value(bytes[2]);
let grind = bytes[3];
let name = ESWeapon::name_from_bytes(&bytes[6..12]);
ESWeapon {
esweapon,
special: special.ok(),
grind,
name,
}
}
// TODO: this isn't even needed. all sranks are rare and only sell for 10? meseta in the shop
pub fn is_rare_item(self) -> bool {
true
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_create_esweapon_from_bytes() {
// JAKESERV BAZOOKA + 137 (Berserk)
let weapon_bytes = [0x00, 0x7E, 0x0C, 0x89, 0x00, 0x00, 0x81, 0x41, 0xAC, 0xB3, 0x96, 0x56, 0x00, 0x00, 0x00, 0x00];
let testweapon = ESWeapon::from_bytes(weapon_bytes);
assert_eq!(testweapon, ESWeapon {
esweapon: ESWeaponType::Bazooka,
special: Some(ESWeaponSpecial::Berserk),
grind: 137u8,
name: "JAKESERV".to_owned(),
});
}
#[test]
fn test_create_bytes_from_esweapon() {
// PSYCHO WAND + 72 (Chaos)
let testweapon = ESWeapon {
esweapon: ESWeaponType::Wand,
special: Some(ESWeaponSpecial::Chaos),
grind: 72u8,
name: "PSYCHO".to_owned(),
};
let bytes = testweapon.as_bytes();
assert_eq!(bytes, [0x00, 0x7B, 0x09, 0x48, 0x00, 0x00, 0x82, 0x13, 0xE4, 0x68, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00]);
}
#[test]
fn test_create_bytes_from_higher_esweapon_type() {
// YUGIOH CARDS + 105 (Spirit)
let testweapon = ESWeapon {
esweapon: ESWeaponType::Cards,
special: Some(ESWeaponSpecial::Spirit),
grind: 105u8,
name: "YUGIOH".to_owned(),
};
let bytes = testweapon.as_bytes();
assert_eq!(bytes, [0x00, 0xA7, 0x0B, 0x69, 0x00, 0x00, 0x83, 0x35, 0x9D, 0x2F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00]);
}
}