// 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, 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); let pkt_cmd = arg.base10_parse::().unwrap(); let parsed = parse_macro_input!(item as ItemStruct); let mut from_bytes = Vec::new(); let mut as_bytes = Vec::new(); let mut dbg_write_vars = Vec::new(); let mut partialeq = Vec::new(); for f in parsed.fields.iter() { if let Some(ident) = &f.ident { let ident_str = ident.to_string(); match &f.ty { syn::Type::Array(arr) => { let array_length = if let syn::Expr::Lit(lit) = &arr.len { if let syn::Lit::Int(int) = &lit.lit { int.base10_parse::().unwrap() } else { return syn::Error::new(arr.bracket_token.span, "unknown array size").to_compile_error().into(); } } else { return syn::Error::new(arr.bracket_token.span, "unknown array size").to_compile_error().into(); }; match *arr.elem { syn::Type::Path(ref path) => { let ty = path.path.segments[0].ident.to_string(); if ty.as_str() == "u8_str" { dbg_write_vars.push(quote! { match std::str::from_utf8(&self.#ident) { Ok(v) => write!(f, " {}: {:?}\n", #ident_str, v).unwrap(), Err(_) => write!(f, " {}: {:?}\n", #ident_str, self.#ident.to_vec()).unwrap() } }); } else { dbg_write_vars.push(quote! { write!(f, " {}: {:?}\n", #ident_str, self.#ident.to_vec()).unwrap(); }); } as_bytes.push(quote! { for f in self.#ident.iter() { buf.extend_from_slice(&f.to_le_bytes()) } }); match ty.as_str() { "u8" | "u8_str" => { from_bytes.push(quote! { #ident: { let mut b: [u8; #array_length] = [0; #array_length]; if let Ok(len) = cur.read(&mut b) { if len != #array_length { return Err(PacketParseError::NotEnoughBytes); } } else { return Err(PacketParseError::NotEnoughBytes); }; b }, }); }, "u16" => { from_bytes.push(quote! { #ident: { let mut b: [u16; #array_length] = [0; #array_length]; if let Ok(len) = cur.read(&mut b) { if len != #array_length { return Err(PacketParseError::NotEnoughBytes); } } else { return Err(PacketParseError::NotEnoughBytes); }; b }, }); }, "u32" => { from_bytes.push(quote! { #ident: { let mut b: [u32; #array_length] = [0; #array_length]; if let Ok(len) = cur.read(&mut b) { if len != #array_length { return Err(PacketParseError::NotEnoughBytes); } } else { return Err(PacketParseError::NotEnoughBytes); }; b }, }); }, _ => { return syn::Error::new(path.path.segments[0].ident.span(), "type not supported") .to_compile_error().into(); } } partialeq.push(quote! { if self.#ident[..] != other.#ident[..] { return false; } }); } _ => { panic!("why"); } } }, syn::Type::Path(path) => { dbg_write_vars.push(quote! { write!(f, " {}: {:?}\n", #ident_str, self.#ident).unwrap(); }); let ty = path.path.segments[0].ident.to_string(); // as_bytes match ty.as_str() { "String" => { as_bytes.push(quote! { for c in self.#ident.as_str().encode_utf16() { buf.extend_from_slice(&c.to_le_bytes()); } }); } _ => { as_bytes.push(quote! { buf.extend_from_slice(&self.#ident.to_le_bytes()); }); } } // from_bytes match ty.as_str() { "u8" | "u8_str" => { from_bytes.push(quote! { #ident: { let mut b: [u8; 1] = [0; 1]; if let Ok(len) = cur.read(&mut b) { if len != 1 { return Err(PacketParseError::NotEnoughBytes); } } else { return Err(PacketParseError::NotEnoughBytes); }; b[0] }, }); }, "u16" => { from_bytes.push(quote! { #ident: { let mut b: [u8; 2] = [0; 2]; if let Ok(len) = cur.read(&mut b) { if len != 2 { return Err(PacketParseError::NotEnoughBytes); } } else { return Err(PacketParseError::NotEnoughBytes); }; u16::from_le_bytes(b) }, }); }, "u32" => { from_bytes.push(quote! { #ident: { let mut b: [u8; 4] = [0; 4]; if let Ok(len) = cur.read(&mut b) { if len != 4 { return Err(PacketParseError::NotEnoughBytes); } } else { return Err(PacketParseError::NotEnoughBytes); }; u32::from_le_bytes(b) }, }); }, "String" => { from_bytes.push(quote! { #ident: { let mut s: Vec = Vec::new(); if let Ok(len) = cur.read_to_end(&mut s) { } else { return Err(PacketParseError::NotEnoughBytes); }; let mut utf16 = Vec::new(); for c in s.chunks(2) { utf16.push(u16::from_le_bytes([c[0], c[1]])); } String::from_utf16_lossy(utf16.as_slice()) }, }); }, _ => { from_bytes.push(quote! { #ident: { let mut b: [u8; #path::SIZE] = [0; #path::SIZE]; if let Ok(len) = cur.read(&mut b) { if len != #path::SIZE { return Err(PacketParseError::NotEnoughBytes); } } else { return Err(PacketParseError::NotEnoughBytes); }; #path::from_le_bytes(b)? }, }); } } partialeq.push(quote! { if self.#ident != other.#ident { return false; } }); } _ => { } } } } let this_struct = parsed.ident.clone(); let this_struct_str = this_struct.to_string(); let psopacket = quote! { impl PSOPacket for #this_struct { fn from_bytes(data: &[u8]) -> Result<#this_struct, 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); if cmd != #pkt_cmd { return Err(PacketParseError::WrongPacketCommand); } if len as usize != data.len() { return Err(PacketParseError::WrongPacketSize(len, data.len())); } let result = Ok(#this_struct { #(#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 = Vec::new(); #(#as_bytes)* while buf.len() % 4 != 0 { buf.push(0); } let pkt_len = (buf.len() + 4) 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)); prebuf.append(&mut buf); prebuf } } }; let psopacket_debug = quote! { impl std::fmt::Debug for #this_struct { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "packet {} {{\n", #this_struct_str).unwrap(); #(#dbg_write_vars)* write!(f, "}}") } } }; let psopacket_partialeq = quote! { impl std::cmp::PartialEq for #this_struct { fn eq(&self, other: &Self) -> bool { #(#partialeq)* true } } }; let q = quote! { #[derive(Clone)] #parsed #psopacket #psopacket_debug #psopacket_partialeq }; //println!("[[[{}]]]", q.to_string()); q.into() } #[proc_macro_attribute] pub fn game_command(attr: TokenStream, item: TokenStream) -> TokenStream { item }