use std::io::{Seek, SeekFrom}; use psopacket::pso_message; use crate::{PSOPacketData, PacketParseError}; pub trait PSOMessage { const CMD: u8; fn from_bytes(cur: &mut R) -> Result where Self: Sized; fn as_bytes(&self) -> Vec; } #[pso_message(0x40)] pub struct PlayerWalking { x: f32, y: f32, z: f32, } #[pso_message(0x42)] pub struct PlayerRunning { x: f32, z: f32, } #[pso_message(0x3E)] pub struct PlayerStopped { unknown1: u32, unknown2: u32, x: f32, y: f32, z: f32, } #[pso_message(0x1F)] pub struct PlayerChangedMap { unknown1: u32, unknown2: u32, x: f32, y: f32, z: f32, } #[pso_message(0x1F)] pub struct PlayerChangedMap2 { unknown1: u32, } #[pso_message(0x20)] pub struct TellOtherPlayerMyLocation { unknown1: u32, unknown2: u32, x: f32, y: f32, z: f32, } #[pso_message(0x3F)] pub struct PlayerLoadedIn { unknown1: u32, unknown2: u32, x: f32, y: f32, z: f32, } #[pso_message(0x23)] pub struct PlayerDoneChangingMap { } #[pso_message(0x75)] pub struct PlayerChangedFloor { map: u32, } #[pso_message(0x52)] pub struct TalkToNpc { unknown1: u32, unknown2: u32, } #[pso_message(0x58)] pub struct LobbyEmote { emote: u32, } #[pso_message(0xAE)] pub struct UnknownAE { unknown1: u32, unknown2: u32, unknown3: u32, } #[pso_message(0xAB)] pub struct PhotonChairSit { unknown1: u32, } #[pso_message(0xAF)] pub struct PhotonChairTurn { unknown1: u32, } #[pso_message(0xB0)] pub struct PhotonChairMove { unknown1: u32, } #[pso_message(0x3B)] pub struct PlayerSpawnedIntoArea { } #[pso_message(0xC8)] pub struct RequestExp { enemy_id: u16, client_id: u8, unused: u8, last_hitter: u32, } #[pso_message(0x60)] pub struct RequestItem { area: u8, pt_index: u8, enemy_id: u16, x: f32, z: f32, y: f32, } #[pso_message(0x76)] pub struct KillMonster { _unknown: [u8; 4], } #[pso_message(0x43)] pub struct ComboStep1 { } #[pso_message(0x44)] pub struct ComboStep2 { } #[pso_message(0x45)] pub struct ComboStep3 { } #[pso_message(0x46)] pub struct ComboStepDone { } #[pso_message(0x8D)] pub struct PlayerTechStart { } #[pso_message(0x47)] pub struct PlayerTechCast { technique: u8, unknown1: u8, unknown2: u8, num_of_targets: u8, } #[pso_message(0x48)] pub struct PlayerTechDone { } #[pso_message(0x0A)] pub struct MobAttack { } #[pso_message(0x4A)] pub struct PlayerBlockedDamage { } #[pso_message(0x83)] pub struct PlayerTrapSet { trap_type: u16, trap_count: u16, } #[pso_message(0x80)] pub struct PlayerTrapActivate { } #[pso_message(0x06)] pub struct GuildcardSend { } #[pso_message(0x06)] pub struct GuildcardRecv { guildcard: u32, name: [u16; 0x18], team: [u16; 0x10], desc: [u16; 0x58], one: u8, language: u8, section_id: u8, class: u8, } #[derive(Debug, Clone, PartialEq)] pub enum GameMessage { PlayerWalking(PlayerWalking), PlayerRunning(PlayerRunning), PlayerStopped(PlayerStopped), PlayerLoadedIn(PlayerLoadedIn), PlayerChangedMap(PlayerChangedMap), PlayerChangedMap2(PlayerChangedMap2), PlayerDoneChangingMap(PlayerDoneChangingMap), PlayerChangedFloor(PlayerChangedFloor), TellOtherPlayerMyLocation(TellOtherPlayerMyLocation), LobbyEmote(LobbyEmote), TalkToNpc(TalkToNpc), UnknownAE(UnknownAE), PhotonChairSit(PhotonChairSit), PhotonChairTurn(PhotonChairTurn), PhotonChairMove(PhotonChairMove), PlayerSpawnedIntoArea(PlayerSpawnedIntoArea), RequestExp(RequestExp), RequestItem(RequestItem), KillMonster(KillMonster), ComboStep1(ComboStep1), ComboStep2(ComboStep2), ComboStep3(ComboStep3), ComboStepDone(ComboStepDone), PlayerTechStart(PlayerTechStart), PlayerTechCast(PlayerTechCast), PlayerTechDone(PlayerTechDone), MobAttack(MobAttack), PlayerBlockedDamage(PlayerBlockedDamage), PlayerTrapSet(PlayerTrapSet), PlayerTrapActivate(PlayerTrapActivate), GuildcardSend(GuildcardSend), GuildcardRecv(GuildcardRecv), } impl PSOPacketData for GameMessage { fn from_bytes(mut cur: &mut R) -> Result { let mut byte = [0u8; 1]; let mut len = [0u8; 1]; cur.read(&mut byte); cur.read(&mut len); cur.seek(SeekFrom::Current(-2)); // Cursor doesn't implement Peek? match byte[0] { PlayerWalking::CMD => Ok(GameMessage::PlayerWalking(PlayerWalking::from_bytes(&mut cur)?)), PlayerRunning::CMD => Ok(GameMessage::PlayerRunning(PlayerRunning::from_bytes(&mut cur)?)), PlayerStopped::CMD => Ok(GameMessage::PlayerStopped(PlayerStopped::from_bytes(&mut cur)?)), PlayerLoadedIn::CMD => Ok(GameMessage::PlayerLoadedIn(PlayerLoadedIn::from_bytes(&mut cur)?)), PlayerChangedMap::CMD if len[0] == 6 => Ok(GameMessage::PlayerChangedMap(PlayerChangedMap::from_bytes(&mut cur)?)), PlayerChangedMap2::CMD if len[0] == 2 => Ok(GameMessage::PlayerChangedMap2(PlayerChangedMap2::from_bytes(&mut cur)?)), PlayerDoneChangingMap::CMD => Ok(GameMessage::PlayerDoneChangingMap(PlayerDoneChangingMap::from_bytes(&mut cur)?)), PlayerChangedFloor::CMD => Ok(GameMessage::PlayerChangedFloor(PlayerChangedFloor::from_bytes(&mut cur)?)), TellOtherPlayerMyLocation::CMD => Ok(GameMessage::TellOtherPlayerMyLocation(TellOtherPlayerMyLocation::from_bytes(&mut cur)?)), LobbyEmote::CMD => Ok(GameMessage::LobbyEmote(LobbyEmote::from_bytes(&mut cur)?)), TalkToNpc::CMD => Ok(GameMessage::TalkToNpc(TalkToNpc::from_bytes(&mut cur)?)), UnknownAE::CMD => Ok(GameMessage::UnknownAE(UnknownAE::from_bytes(&mut cur)?)), PhotonChairSit::CMD => Ok(GameMessage::PhotonChairSit(PhotonChairSit::from_bytes(&mut cur)?)), PhotonChairTurn::CMD => Ok(GameMessage::PhotonChairTurn(PhotonChairTurn::from_bytes(&mut cur)?)), PhotonChairMove::CMD => Ok(GameMessage::PhotonChairMove(PhotonChairMove::from_bytes(&mut cur)?)), PlayerSpawnedIntoArea::CMD => Ok(GameMessage::PlayerSpawnedIntoArea(PlayerSpawnedIntoArea::from_bytes(&mut cur)?)), RequestExp::CMD => Ok(GameMessage::RequestExp(RequestExp::from_bytes(&mut cur)?)), RequestItem::CMD => Ok(GameMessage::RequestItem(RequestItem::from_bytes(&mut cur)?)), KillMonster::CMD => Ok(GameMessage::KillMonster(KillMonster::from_bytes(&mut cur)?)), ComboStep1::CMD => Ok(GameMessage::ComboStep1(ComboStep1::from_bytes(&mut cur)?)), ComboStep2::CMD => Ok(GameMessage::ComboStep2(ComboStep2::from_bytes(&mut cur)?)), ComboStep3::CMD => Ok(GameMessage::ComboStep3(ComboStep3::from_bytes(&mut cur)?)), ComboStepDone::CMD => Ok(GameMessage::ComboStepDone(ComboStepDone::from_bytes(&mut cur)?)), PlayerTechStart::CMD => Ok(GameMessage::PlayerTechStart(PlayerTechStart::from_bytes(&mut cur)?)), PlayerTechCast::CMD => Ok(GameMessage::PlayerTechCast(PlayerTechCast::from_bytes(&mut cur)?)), PlayerTechDone::CMD => Ok(GameMessage::PlayerTechDone(PlayerTechDone::from_bytes(&mut cur)?)), MobAttack::CMD => Ok(GameMessage::MobAttack(MobAttack::from_bytes(&mut cur)?)), PlayerBlockedDamage::CMD => Ok(GameMessage::PlayerBlockedDamage(PlayerBlockedDamage::from_bytes(&mut cur)?)), PlayerTrapSet::CMD => Ok(GameMessage::PlayerTrapSet(PlayerTrapSet::from_bytes(&mut cur)?)), PlayerTrapActivate::CMD => Ok(GameMessage::PlayerTrapActivate(PlayerTrapActivate::from_bytes(&mut cur)?)), GuildcardSend::CMD => Ok(GameMessage::GuildcardSend(GuildcardSend::from_bytes(&mut cur)?)), GuildcardRecv::CMD => Ok(GameMessage::GuildcardRecv(GuildcardRecv::from_bytes(&mut cur)?)), _ => Err(PacketParseError::UnknownMessage(byte[0], { let mut b = vec![0; len[0] as usize * 4]; cur.read(&mut b).unwrap(); b.to_vec() } )), } } fn as_bytes(&self) -> Vec { match self { GameMessage::PlayerWalking(data) => data.as_bytes(), GameMessage::PlayerRunning(data) => data.as_bytes(), GameMessage::PlayerStopped(data) => data.as_bytes(), GameMessage::PlayerLoadedIn(data) => data.as_bytes(), GameMessage::PlayerChangedMap(data) => data.as_bytes(), GameMessage::PlayerChangedMap2(data) => data.as_bytes(), GameMessage::PlayerDoneChangingMap(data) => data.as_bytes(), GameMessage::TellOtherPlayerMyLocation(data) => data.as_bytes(), GameMessage::LobbyEmote(data) => data.as_bytes(), GameMessage::TalkToNpc(data) => data.as_bytes(), GameMessage::UnknownAE(data) => data.as_bytes(), GameMessage::PhotonChairSit(data) => data.as_bytes(), GameMessage::PhotonChairTurn(data) => data.as_bytes(), GameMessage::PhotonChairMove(data) => data.as_bytes(), GameMessage::PlayerSpawnedIntoArea(data) => data.as_bytes(), GameMessage::RequestExp(data) => data.as_bytes(), GameMessage::RequestItem(data) => data.as_bytes(), GameMessage::KillMonster(data) => data.as_bytes(), GameMessage::ComboStep1(data) => data.as_bytes(), GameMessage::ComboStep2(data) => data.as_bytes(), GameMessage::ComboStep3(data) => data.as_bytes(), GameMessage::ComboStepDone(data) => data.as_bytes(), GameMessage::PlayerTechStart(data) => data.as_bytes(), GameMessage::PlayerTechCast(data) => data.as_bytes(), GameMessage::PlayerTechDone(data) => data.as_bytes(), GameMessage::MobAttack(data) => data.as_bytes(), GameMessage::PlayerBlockedDamage(data) => data.as_bytes(), GameMessage::PlayerTrapSet(data) => data.as_bytes(), GameMessage::PlayerTrapActivate(data) => data.as_bytes(), GameMessage::PlayerChangedFloor(data) => data.as_bytes(), GameMessage::GuildcardSend(data) => data.as_bytes(), GameMessage::GuildcardRecv(data) => data.as_bytes(), } } }