|
@ -1,11 +1,374 @@ |
|
|
|
|
|
// TODO: try less hard, just mem::transmute all this junk away
|
|
|
|
|
|
// TODO: PSOPacketData derive
|
|
|
|
|
|
|
|
|
#![recursion_limit="256"]
|
|
|
#![recursion_limit="256"]
|
|
|
|
|
|
|
|
|
extern crate proc_macro;
|
|
|
extern crate proc_macro;
|
|
|
|
|
|
|
|
|
use proc_macro::TokenStream;
|
|
|
use proc_macro::TokenStream;
|
|
|
use syn::{parse_macro_input, ItemStruct};
|
|
|
|
|
|
|
|
|
use syn::{parse_macro_input, ItemStruct, Meta, NestedMeta, Attribute};
|
|
|
use quote::quote;
|
|
|
use quote::quote;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
|
enum AttrMeta {
|
|
|
|
|
|
Utf8,
|
|
|
|
|
|
Utf16,
|
|
|
|
|
|
NoDebug,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
|
enum AttrType {
|
|
|
|
|
|
Value(syn::PathSegment, syn::Ident, Option<AttrMeta>),
|
|
|
|
|
|
Array(syn::PathSegment, syn::Ident, usize, Option<AttrMeta>),
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn generate_struct_def(name: syn::Ident, attrs: &Vec<AttrType>) -> 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<AttrType>, 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<u8> {
|
|
|
|
|
|
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<u8> = 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<AttrType>) -> 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<AttrType>) -> 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]
|
|
|
#[proc_macro_attribute]
|
|
|
pub fn pso_packet(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|
|
pub fn pso_packet(attr: TokenStream, item: TokenStream) -> TokenStream {
|
|
|
let arg = parse_macro_input!(attr as syn::LitInt);
|
|
|
let arg = parse_macro_input!(attr as syn::LitInt);
|
|
|