diff --git a/psopacket/src/lib.rs b/psopacket/src/lib.rs index 27f897a..fc26bcb 100644 --- a/psopacket/src/lib.rs +++ b/psopacket/src/lib.rs @@ -16,6 +16,8 @@ enum AttrMeta { Utf8, Utf16, NoDebug, + LengthIs(syn::Ident), + LengthOf(syn::Ident), } #[derive(Debug)] @@ -237,9 +239,9 @@ fn generate_debug_impl(name: syn::Ident, attrs: &Vec) -> proc_macro2:: AttrMeta::NoDebug => quote! { write!(f, " {} [{}; {}]: [...]\n", #ident_str, #type_str, #len)?; }, - AttrMeta::None => quote! { + _ => quote! { write!(f, " {} [{}; {}]: {:?}\n", #ident_str, #type_str, #len, self.#name.to_vec())?; - } + }, } } }; @@ -306,6 +308,8 @@ fn get_struct_fields(fields: Iter) -> Result, TokenStream> "utf8" => AttrMeta::Utf8, "utf16" => AttrMeta::Utf16, "nodebug" => AttrMeta::NoDebug, + "length_is" => AttrMeta::LengthIs(attr.parse_args::().unwrap()), + "length_of" => AttrMeta::LengthOf(attr.parse_args::().unwrap()), _ => AttrMeta::None } } @@ -404,9 +408,130 @@ pub fn pso_packet(attr: TokenStream, item: TokenStream) -> TokenStream { q.into() } + +fn generate_psomessage_from_bytes(attrs: &Vec) -> (Vec, Vec) { + //let mut from_bytes = Vec::new(); + let mut assignment = Vec::new(); + let mut structure_creation = Vec::new(); + for attr in attrs { + let (assignment_element, creation_element) = match attr { + AttrType::Value(ty, name, meta) => { + let temp_name = syn::Ident::new(&("_".to_string() + &name.to_string()), name.span()); + let type_str = ty.path.segments[0].ident.to_string(); + if type_str == "Vec" { + let vec_type = match &ty.path.segments[0].arguments { + syn::PathArguments::AngleBracketed(arg) => { + match &arg.args[0] { + syn::GenericArgument::Type(typ) => { + match &typ { + syn::Type::Path(path) => { + Some(path.path.segments[0].ident.clone()) + } + _ => None + } + } + _ => None, + } + } + _ => None + }.unwrap(); + + let length_variable = match meta { + AttrMeta::LengthIs(ident) => syn::Ident::new(&("_".to_string() + &ident.to_string()), ident.span()), + _ => panic!("{} does not have a length specified", name), + }; + let assignment = quote! { + let mut #temp_name = Vec::new(); + + for _ in 0..#length_variable { + #temp_name.push(<#vec_type as PSOPacketData>::from_bytes(&mut cur)?); + } + }; + let creation = quote! { + #name: #temp_name, + }; + + (assignment, creation) + } + else { + let assignment = quote! { + let #temp_name = <#ty as PSOPacketData>::from_bytes(&mut cur)?; + }; + let creation = quote! { + #name: #temp_name, + }; + + (assignment, creation) + } + }, + AttrType::Array(ty, name, len, _) => { + let temp_name = syn::Ident::new(&("_".to_string() + &name.to_string()), name.span()); + let assignment = quote! { + let #temp_name = { + let mut arr = [#ty::default(); #len]; + for e in arr.iter_mut() { + *e = #ty::from_bytes(&mut cur)? + } + arr + }; + }; + let creation = quote! { + #name: #temp_name, + }; + (assignment, creation) + } + }; + assignment.push(assignment_element); + structure_creation.push(creation_element); + } + (assignment, structure_creation) +} + +fn generate_psomessage_as_bytes(attrs: &Vec) -> Vec { + let mut as_bytes = Vec::new(); + for attr in attrs { + let element = match attr { + AttrType::Value(ty, name, meta) => { + let type_str = ty.path.segments[0].ident.to_string(); + if type_str == "Vec" { + quote! { + for i in self.#name.iter() { + buf.extend_from_slice(&PSOPacketData::as_bytes(i)); + } + } + } + else { + if let AttrMeta::LengthOf(ident) = meta { + quote! { + buf.extend_from_slice(&PSOPacketData::as_bytes(&(self.#ident.len() as #ty))); + } + } + else { + quote! { + buf.extend_from_slice(&PSOPacketData::as_bytes(&self.#name)); + } + } + } + }, + AttrType::Array(_ty, name, len, _) => { + quote! { + for i in 0..#len { + buf.extend_from_slice(&self.#name[i].as_bytes()); + } + } + } + }; + as_bytes.push(element); + } + as_bytes +} + + + + fn generate_psomessage_impl(msg_cmd: u8, name: syn::Ident, attrs: &Vec) -> proc_macro2::TokenStream { - let from_bytes = generate_from_bytes(&attrs); - let as_bytes = generate_as_bytes(&attrs); + let (assignment, struct_creation)= generate_psomessage_from_bytes(&attrs); + let as_bytes = generate_psomessage_as_bytes(&attrs); quote! { impl PSOMessage for #name { @@ -430,8 +555,9 @@ fn generate_psomessage_impl(msg_cmd: u8, name: syn::Ident, attrs: &Vec } let mut cur = std::io::Cursor::new(subbuf); + #(#assignment)* let result = Ok(#name { - #(#from_bytes)* + #(#struct_creation)* }); result diff --git a/src/lib.rs b/src/lib.rs index 059c502..1ff7c69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -580,4 +580,42 @@ mod test { let data = SixDee::from_bytes(&pkt); assert!(pkt == data.unwrap().as_bytes()); } + + #[test] + fn test_message_length() { + #[pso_message(0x23)] + struct Lengths { + a: u8, + #[length_of(d)] + b: u8, + c: u8, + #[length_is(b)] + d: Vec, + } + + let pkt = Lengths { + client: 1, + target: 2, + a: 12, + b: 23, + c: 34, + d: vec![9,9,9], + }; + + let mut data = pkt.as_bytes(); + assert!(data == vec![35, 3, 1, 2, 12, 3, 34, 9, 9, 9, 0, 0]); + + data[10] = 8; + data[5] = 4; + + let l = Lengths::from_bytes(&mut std::io::Cursor::new(data)).unwrap(); + assert!(l == Lengths { + client: 1, + target: 2, + a: 12, + b: 4, + c: 34, + d: vec![9,9,9,8], + }); + } } diff --git a/src/packet/messages.rs b/src/packet/messages.rs index 7a46b86..248fd6e 100644 --- a/src/packet/messages.rs +++ b/src/packet/messages.rs @@ -1,7 +1,7 @@ #![allow(unused_must_use)] use std::io::{SeekFrom}; -use psopacket::pso_message; +use psopacket::{pso_message, PSOPacketData}; use crate::{PSOPacketData, PacketParseError};