diff --git a/examples/capture_parser.rs b/examples/capture_parser.rs index 6f07726..ac3456d 100644 --- a/examples/capture_parser.rs +++ b/examples/capture_parser.rs @@ -73,7 +73,6 @@ fn parse(path: &Path) -> Res { && !file.name().contains("[53-05-00-06]") && !file.name().contains("[53-05-00-15]") && !file.name().contains("[53-05-00-1c]") - && !file.name().contains("[53-05-00-1e]") && !file.name().contains("[53-05-00-1f]") && !file.name().contains("[53-05-00-22]") && !file.name().contains("[53-05-00-31]") diff --git a/src/common/mod.rs b/src/common/mod.rs index 0798b01..c0273cc 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -16,9 +16,9 @@ pub use self::str::*; Note: the length type is not checked and the `Vec` still uses `usize` internally. Handle with care. */ #[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct LVec(Vec, PhantomData); +pub struct LVec(Vec, PhantomData); -impl LVec { +impl LVec { pub fn new() -> Self { Self(Vec::new(), PhantomData) } @@ -31,46 +31,48 @@ impl LVec { &self.0 } - pub(crate) fn deser_content(reader: &mut R, str_len: L) -> Res where L: TryInto { - let str_len = match str_len.try_into() { + pub(crate) fn deser_content(reader: &mut R, len: L) -> Res where L: TryInto, T: Deserialize { + let len = match len.try_into() { Ok(x) => x, _ => panic!(), }; - let mut vec = Vec::::with_capacity(str_len); - unsafe { - vec.set_len(str_len); - let mut ucs2_str_slice = std::slice::from_raw_parts_mut(vec.as_mut_ptr() as *mut u8, str_len*std::mem::size_of::()); - Read::read(reader, &mut ucs2_str_slice)?; + let mut vec = Vec::::with_capacity(len); + for _ in 0..len { + vec.push(LERead::read(reader)?); } Ok(Self(vec, PhantomData)) } pub(crate) fn ser_len(&self, writer: &mut W) -> Res<()> where L: TryFrom + Serialize { - let str_len = self.0.len(); - let l_len = match L::try_from(str_len) { + let len = self.0.len(); + let l_len = match L::try_from(len) { Ok(x) => x, _ => panic!(), }; writer.write(l_len) } - pub(crate) fn ser_content(&self, writer: &mut W) -> Res<()> { - let u8_slice = unsafe { std::slice::from_raw_parts(self.0.as_ptr() as *const u8, self.0.len()*std::mem::size_of::()) }; - writer.write_all(u8_slice) + pub(crate) fn ser_content(&self, writer: &mut W) -> Res<()> where for<'a> &'a T: Serialize { + for e in &self.0 { + LEWrite::write(writer, e)?; + } + Ok(()) } } -impl Deserialize for LVec - where L: TryInto + Deserialize { +impl Deserialize for LVec + where L: TryInto + Deserialize, + T: Deserialize { fn deserialize(reader: &mut R) -> Res { - let str_len: L = LERead::read(reader)?; - Self::deser_content(reader, str_len) + let len: L = LERead::read(reader)?; + Self::deser_content(reader, len) } } -impl<'a, T, L, W: Write> Serialize for &'a LVec - where L: TryFrom + Serialize { +impl<'a, T, L, W: Write> Serialize for &'a LVec + where L: TryFrom + Serialize, + for<'b> &'b T: Serialize { fn serialize(self, writer: &mut W) -> Res<()> { self.ser_len(writer)?; @@ -78,7 +80,7 @@ impl<'a, T, L, W: Write> Serialize for &'a LVec } } -impl std::ops::Deref for LVec { +impl std::ops::Deref for LVec { type Target = Vec; #[inline] @@ -87,14 +89,14 @@ impl std::ops::Deref for LVec { } } -impl std::ops::DerefMut for LVec { +impl std::ops::DerefMut for LVec { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } -impl From> for LVec { +impl From> for LVec { fn from(vec: Vec) -> Self { Self(vec, PhantomData) } diff --git a/src/common/str/mod.rs b/src/common/str/mod.rs index ebb6877..69ebf0a 100644 --- a/src/common/str/mod.rs +++ b/src/common/str/mod.rs @@ -1,6 +1,8 @@ mod fixed; mod variable; +use endio::{Deserialize, Serialize}; + pub use self::fixed::*; pub use self::variable::*; @@ -15,9 +17,9 @@ pub struct AsciiError; #[derive(Debug)] pub struct Ucs2Error; -#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct AsciiChar(u8); -#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Copy, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] pub struct Ucs2Char(u16); impl LuChar for AsciiChar { diff --git a/src/common/str/variable.rs b/src/common/str/variable.rs index a99c3d1..9d06728 100644 --- a/src/common/str/variable.rs +++ b/src/common/str/variable.rs @@ -4,8 +4,8 @@ use std::marker::PhantomData; use crate::common::LVec; use super::{AsciiChar, AsciiError, LuStrExt, LuWStr, Ucs2Char, Ucs2Error}; -pub type LuVarString = LVec; -pub type LuVarWString = LVec; +pub type LuVarString = LVec; +pub type LuVarWString = LVec; impl std::fmt::Debug for LuVarString { fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { diff --git a/src/world/client/mod.rs b/src/world/client/mod.rs index 69b1c3b..11cebf1 100644 --- a/src/world/client/mod.rs +++ b/src/world/client/mod.rs @@ -58,6 +58,7 @@ pub enum ClientMessage { TransferToWorld(TransferToWorld) = 14, BlueprintLoadItemResponse(BlueprintLoadItemResponse) = 23, AddFriendRequest(AddFriendRequest) = 27, + GetFriendsListResponse(GetFriendsListResponse) = 30, TeamInvite(TeamInvite) = 35, MinimumChatModeResponse(MinimumChatModeResponse) = 57, MinimumChatModeResponsePrivate(MinimumChatModeResponsePrivate) = 58, @@ -187,7 +188,7 @@ pub struct CharListChar { #[padding=4] pub last_location: ZoneId, #[padding=8] - pub equipped_items: LVec, + pub equipped_items: LVec, } /** @@ -281,6 +282,26 @@ pub struct AddFriendRequest { pub is_best_friend_request: bool, } +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[trailing_padding=6] +pub struct FriendState { + is_online: bool, + is_best_friend: bool, + is_free_trial: bool, + #[padding=5] + location: ZoneId, + object_id: ObjId, + char_name: LuWString33, +} + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[repr(u8)] +#[post_disc_padding=2] +pub enum GetFriendsListResponse { + Ok(LVec), + GeneralError, +} + /** Informs the client that another player has asked them to be their friend. diff --git a/src/world/client/tests/GetFriendsListResponse.bin b/src/world/client/tests/GetFriendsListResponse.bin new file mode 100644 index 0000000..ebdc5d1 Binary files /dev/null and b/src/world/client/tests/GetFriendsListResponse.bin differ diff --git a/src/world/client/tests/GetFriendsListResponse.rs b/src/world/client/tests/GetFriendsListResponse.rs new file mode 100644 index 0000000..3e71b9f --- /dev/null +++ b/src/world/client/tests/GetFriendsListResponse.rs @@ -0,0 +1,138 @@ +ClientMessage::GetFriendsListResponse( + GetFriendsListResponse::Ok( + vec![ + FriendState { + is_online: false, + is_best_friend: false, + is_free_trial: false, + location: ZoneId { + map_id: 0, + instance_id: 0, + clone_id: 0, + }, + object_id: 1152921508123877941, + char_name: lu!("Stingrod"), + }, + FriendState { + is_online: false, + is_best_friend: false, + is_free_trial: false, + location: ZoneId { + map_id: 0, + instance_id: 0, + clone_id: 0, + }, + object_id: 1152921508691197351, + char_name: lu!("scaredycat12"), + }, + FriendState { + is_online: false, + is_best_friend: false, + is_free_trial: true, + location: ZoneId { + map_id: 0, + instance_id: 0, + clone_id: 0, + }, + object_id: 1152921507861167019, + char_name: lu!("DivineDinoKing"), + }, + FriendState { + is_online: false, + is_best_friend: false, + is_free_trial: false, + location: ZoneId { + map_id: 0, + instance_id: 0, + clone_id: 0, + }, + object_id: 1152921507552150810, + char_name: lu!("ScottishBeast"), + }, + FriendState { + is_online: false, + is_best_friend: false, + is_free_trial: false, + location: ZoneId { + map_id: 0, + instance_id: 0, + clone_id: 0, + }, + object_id: 1152921507612937007, + char_name: lu!("Forkstealer"), + }, + FriendState { + is_online: false, + is_best_friend: false, + is_free_trial: false, + location:ZoneId { + map_id: 0, + instance_id: 0, + clone_id: 0, + }, + object_id: 1152921507702853643, + char_name: lu!("princebrick") + }, + FriendState { + is_online: false, + is_best_friend: false, + is_free_trial: true, + location: ZoneId { + map_id: 0, + instance_id: 0, + clone_id: 0, + }, + object_id: 1152921507778387642, + char_name: lu!("CosmicFinestHerder"), + }, + FriendState { + is_online: false, + is_best_friend: false, + is_free_trial: false, + location: ZoneId { + map_id: 0, + instance_id: 0, + clone_id: 0, + }, + object_id: 1152921508146466976, + char_name: lu!("felito"), + }, + FriendState { + is_online: false, + is_best_friend: false, + is_free_trial: false, + location: ZoneId { + map_id: 0, + instance_id: 0, + clone_id: 0, + }, + object_id: 1152921509260170524, + char_name: lu!("FrecklyCrunchEarlobe"), + }, + FriendState { + is_online: false, + is_best_friend: false, + is_free_trial: false, + location: ZoneId { + map_id: 0, + instance_id: 0, + clone_id: 0, + }, + object_id: 1152921507960010807, + char_name: lu!("BreezyFlyingNinja"), + }, + FriendState { + is_online: false, + is_best_friend: false, + is_free_trial: false, + location: ZoneId { + map_id: 0, + instance_id: 0, + clone_id: 0, + }, + object_id: 1152921509697915304, + char_name: lu!("PikachuThunder"), + }, + ].into(), + ), +) \ No newline at end of file