diff --git a/psopacket/src/lib.rs b/psopacket/src/lib.rs index c12821e..f9ed7a4 100644 --- a/psopacket/src/lib.rs +++ b/psopacket/src/lib.rs @@ -1,11 +1,374 @@ +// TODO: try less hard, just mem::transmute all this junk away +// TODO: PSOPacketData derive + #![recursion_limit="256"] extern crate proc_macro; use proc_macro::TokenStream; -use syn::{parse_macro_input, ItemStruct}; +use syn::{parse_macro_input, ItemStruct, Meta, NestedMeta, Attribute}; use quote::quote; + + +#[derive(Debug)] +enum AttrMeta { + Utf8, + Utf16, + NoDebug, +} + + + +#[derive(Debug)] +enum AttrType { + Value(syn::PathSegment, syn::Ident, Option), + Array(syn::PathSegment, syn::Ident, usize, Option), +} + +fn generate_struct_def(name: syn::Ident, attrs: &Vec) -> proc_macro2::TokenStream { + let mut struct_def = Vec::new(); + for attr in attrs { + let element = match attr { + AttrType::Value(ty, name, _) => { + quote!(#name: #ty) + }, + AttrType::Array(ty, name, len, _) => { + quote!(#name: [#ty; #len]) + } + }; + struct_def.push(element); + } + + quote! { + struct #name { + #(#struct_def),* + } + } +} + + +fn generate_psopacket_impl(pkt_cmd: u16, name: syn::Ident, attrs: &Vec, include_flag: bool) -> proc_macro2::TokenStream { + let mut from_bytes = Vec::new(); + for attr in attrs { + let element = match attr { + AttrType::Value(ty, name, _) => { + let type_str = ty.ident.to_string(); + if type_str == "Vec" { + let vec_type = match &ty.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(); + + quote! { + #name: { + let mut tmp = Vec::new(); + + for _ in 0..flag { + //#name: <#ty as PSOPacketData>::from_bytes(&mut cur)?, + tmp.push(<#vec_type as PSOPacketData>::from_bytes(&mut cur)?); + } + tmp + } + } + } + else { + quote! { + #name: <#ty as PSOPacketData>::from_bytes(&mut cur)?, + } + } + }, + AttrType::Array(ty, name, len, _) => { + let array_init = (0..*len).map(|_k| quote! { + #ty::from_bytes(&mut cur)?, + }); + quote! { + #name: [#(#array_init)*], + } + } + }; + from_bytes.push(element); + } + + let mut as_bytes = Vec::new(); + for attr in attrs { + let element = match attr { + AttrType::Value(ty, name, _) => { + let type_str = ty.ident.to_string(); + if type_str == "Vec" { + quote! { + flag = self.#name.len() as u32; + for i in self.#name.iter() { + buf.extend_from_slice(&PSOPacketData::as_bytes(i)); + } + } + } + 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); + } + + quote! { + impl PSOPacket for #name { + fn from_bytes(data: &[u8]) -> Result<#name, PacketParseError> { + let mut cur = std::io::Cursor::new(data); + let mut b: [u8; 2] = [0; 2]; + cur.read(&mut b).unwrap(); + let len = u16::from_le_bytes(b); + cur.read(&mut b).unwrap(); + let cmd = u16::from_le_bytes(b); + let mut f: [u8; 4] = [0; 4]; + + let flag = if #include_flag { + cur.read(&mut f).unwrap(); + u32::from_le_bytes(f) + } + else { 0 }; + + if cmd != #pkt_cmd { + return Err(PacketParseError::WrongPacketCommand); + } + + if len as usize != data.len() { + return Err(PacketParseError::WrongPacketSize(len, data.len())); + } + + let result = Ok(#name { + #(#from_bytes)* + }); + + if cur.position() as usize != data.len() { + return Err(PacketParseError::DataStructNotLargeEnough(cur.position(), data.len())); + } + + result + } + + fn as_bytes(&self) -> Vec { + let mut buf = Vec::new(); + let mut flag = 0; + #(#as_bytes)* + + while buf.len() % 4 != 0 { + buf.push(0); + } + + let pkt_len = (buf.len() + 8) as u16; + let mut prebuf: Vec = Vec::new(); + + prebuf.extend_from_slice(&u16::to_le_bytes(pkt_len)); + prebuf.extend_from_slice(&u16::to_le_bytes(#pkt_cmd)); + if #include_flag { + prebuf.extend_from_slice(&u32::to_le_bytes(flag)); + } + prebuf.append(&mut buf); + prebuf + } + } + } +} + +fn generate_debug_impl(name: syn::Ident, attrs: &Vec) -> proc_macro2::TokenStream { + let mut dbg_write = Vec::new(); + for attr in attrs { + let element = match attr { + AttrType::Value(ty, name, _meta) => { + let ident_str = name.to_string(); + let type_str = ty.ident.to_string(); + quote! { + write!(f, " {} {}: {:?}\n", #ident_str, #type_str, self.#name)?; + } + }, + AttrType::Array(ty, name, len, meta) => { + let ident_str = name.to_string(); + let type_str = ty.ident.to_string(); + if let Some(meta) = meta { + match meta { + AttrMeta::Utf8 => { + quote! { + match std::str::from_utf8(&self.#name) { + Ok(v) => write!(f, " {} [utf8; {}]: {:?}\n", #ident_str, #len, v)?, + Err(_) => write!(f, " {} [{}; {}]: {:?}\n", #ident_str, #type_str, #len, self.#name.to_vec())?, + }; + } + }, + AttrMeta::Utf16 => { + quote! { + match String::from_utf16(&self.#name) { + Ok(v) => write!(f, " {} [utf16; {}]: {:?}\n", #ident_str, #len, v)?, + Err(_) => write!(f, " {} [{}; {}]: {:?}\n", #ident_str, #type_str, #len, self.#name.to_vec())?, + }; + } + }, + _ => quote! { write!(f, " {} [{}; {}]: {:?}\n", #ident_str, #type_str, #len, self.#name.to_vec())?; } + } + } + else { + quote! { write!(f, " {} [{}; {}]: {:?}\n", #ident_str, #type_str, #len, self.#name.to_vec())?; } + } + } + }; + dbg_write.push(element); + } + let name_str = name.to_string(); + quote! { + impl std::fmt::Debug for #name { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "packet {} {{\n", #name_str)?; + #(#dbg_write)* + write!(f, "}}") + } + } + } +} + + +fn generate_partialeq_impl(name: syn::Ident, attrs: &Vec) -> proc_macro2::TokenStream { + let mut partialeq = Vec::new(); + for attr in attrs { + let element = match attr { + AttrType::Value(_, name, _) => { + quote! { + if self.#name!= other.#name { + return false; + } + } + }, + AttrType::Array(_, name, _, _) => { + quote! { + if self.#name[..] != other.#name[..] { + return false; + } + } + + } + }; + partialeq.push(element); + } + quote! { + impl std::cmp::PartialEq for #name { + fn eq(&self, other: &Self) -> bool { + #(#partialeq)* + true + } + } + } + +} + +#[proc_macro_attribute] +pub fn pso_packet2(attr: TokenStream, item: TokenStream) -> TokenStream { + let args = parse_macro_input!(attr as syn::AttributeArgs); + let mut cmd = 0; + let mut flag = true; + let mut debug = true; + let mut from_bytes = true; + let mut as_bytes = true; + for a in args { + match &a { + NestedMeta::Lit(lit) => { + if let syn::Lit::Int(litint) = lit { + cmd = litint.base10_parse().unwrap(); + } + }, + NestedMeta::Meta(k) => { + if let syn::Meta::Path(syn::Path {segments, ..}) = k { + match segments[0].ident.to_string().as_str() { + "no_flag" => flag = false, + "no_debug" => debug = false, + "no_from" => from_bytes = false, + "no_as" => as_bytes = false, + _ => { + return syn::Error::new(segments[0].ident.span(), "unknown macro param").to_compile_error().into(); + } + } + } + }, + } + } + + let mut attrs = Vec::new(); + let pkt_struct = parse_macro_input!(item as ItemStruct); + + let mut must_be_last = false; + for field in pkt_struct.fields.iter() { + if must_be_last { + return syn::Error::new(field.ident.as_ref().unwrap().span(), "variables can not follow Vec or String").to_compile_error().into(); + } + let mut attr_meta = None; + for attr in &field.attrs { + attr_meta = match attr.path.segments[0].ident.to_string().as_str() { + "utf8" => Some(AttrMeta::Utf8), + "utf16" => Some(AttrMeta::Utf16), + "nodebug" => Some(AttrMeta::NoDebug), + _ => None + } + } + match &field.ty { + syn::Type::Array(ty) => { + if let (syn::Type::Path(ref ty), syn::Expr::Lit(ref lit)) = (&*ty.elem, &ty.len) { + if let syn::Lit::Int(ref int) = lit.lit { + attrs.push(AttrType::Array(ty.path.segments[0].clone(), + field.ident.as_ref().unwrap().clone(), + int.base10_parse().unwrap(), + attr_meta + )) + } + } + }, + syn::Type::Path(ty) => { + let type_str = ty.path.segments[0].ident.to_string(); + if type_str == "String" || type_str == "Vec"{ + must_be_last = true; + } + attrs.push(AttrType::Value(ty.path.segments[0].clone(), + field.ident.as_ref().unwrap().clone(), + attr_meta)) + }, + _ => {} + } + } + + let struct_def = generate_struct_def(pkt_struct.ident.clone(), &attrs); + let psopacket_impl = generate_psopacket_impl(cmd, pkt_struct.ident.clone(), &attrs, flag); + let debug_impl = generate_debug_impl(pkt_struct.ident.clone(), &attrs); + let partialeq_impl = generate_partialeq_impl(pkt_struct.ident.clone(), &attrs); + + let q = quote!{ + #[derive(Clone)] + #struct_def + #psopacket_impl + #debug_impl + #partialeq_impl + }; + + q.into() +} + #[proc_macro_attribute] pub fn pso_packet(attr: TokenStream, item: TokenStream) -> TokenStream { let arg = parse_macro_input!(attr as syn::LitInt);