Generalize derive macro for arbitrary padding

This commit is contained in:
lcdr
2020-06-20 12:41:11 +02:00
parent 4694931460
commit 348f101ce8
10 changed files with 77 additions and 78 deletions

View File

@@ -5,6 +5,7 @@ mod service_message_d;
mod service_message_s;
use proc_macro::TokenStream;
use syn::{DeriveInput, Lit, LitInt, Meta};
#[proc_macro_derive(FromVariants)]
pub fn derive_from_variants(input: TokenStream) -> TokenStream {
@@ -21,12 +22,34 @@ pub fn derive_gm_type(input: TokenStream) -> TokenStream {
gm_type::derive(input)
}
#[proc_macro_derive(ServiceMessageD)]
#[proc_macro_derive(ServiceMessageD, attributes(disc_padding))]
pub fn derive_service_message_d(input: TokenStream) -> TokenStream {
service_message_d::derive(input)
}
#[proc_macro_derive(ServiceMessageS)]
#[proc_macro_derive(ServiceMessageS, attributes(disc_padding))]
pub fn derive_service_message_s(input: TokenStream) -> TokenStream {
service_message_s::derive(input)
}
fn get_disc_padding(input: &DeriveInput) -> Option<LitInt> {
for attr in &input.attrs {
if !attr.path.is_ident("disc_padding") {
continue;
}
let meta = match attr.parse_meta() {
Err(_) => panic!("encountered unparseable disc_padding attribute"),
Ok(x) => x,
};
let lit = match meta {
Meta::NameValue(x) => x.lit,
_ => panic!("disc_padding needs to be name=value"),
};
let int_lit = match lit {
Lit::Int(x) => x,
_ => panic!("disc_padding needs to be an integer"),
};
return Some(int_lit);
}
None
}

View File

@@ -1,6 +1,8 @@
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{parse_macro_input, parse_quote, Data, DataEnum, DeriveInput, Fields};
use syn::{parse_macro_input, parse_quote, Data, DataEnum, DeriveInput, Fields, LitInt};
use crate::get_disc_padding;
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
@@ -10,7 +12,8 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
};
let name = &input.ident;
let deser_code = gen_deser_code_enum(data, &name);
let disc_padding = get_disc_padding(&input);
let deser_code = gen_deser_code_enum(data, &name, disc_padding);
let deser_impl = &mut input.generics.clone();
deser_impl.params.push(parse_quote!(__READER: ::std::io::Read));
let (deser_impl, _, _) = deser_impl.split_for_impl();
@@ -42,7 +45,7 @@ fn gen_deser_code_fields(fields: &Fields) -> TokenStream {
}
}
fn gen_deser_code_enum(data: &DataEnum, name: &Ident) -> TokenStream {
fn gen_deser_code_enum(data: &DataEnum, name: &Ident, padding: Option<LitInt>) -> TokenStream {
let last_disc: syn::ExprLit = parse_quote! { 0 };
let mut last_disc = &last_disc.into();
let mut disc_offset = 0;
@@ -58,9 +61,16 @@ fn gen_deser_code_enum(data: &DataEnum, name: &Ident) -> TokenStream {
disc_offset += 1;
arms.push(arm);
}
let read_padding = match padding {
Some(x) => quote! {
let mut padding = [0; #x];
::std::io::Read::read_exact(reader, &mut padding)?;
},
None => quote! { },
};
quote! {
let disc: u32 = ::endio::LERead::read(reader)?;
let _padding: u8 = ::endio::LERead::read(reader)?;
#read_padding
Ok(match disc {
#(#arms)*
_ => return ::std::result::Result::Err(::std::io::Error::new(::std::io::ErrorKind::InvalidData, format!("invalid discriminant value for {}: {}", stringify!(#name), disc)))

View File

@@ -1,6 +1,8 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use syn::{parse_macro_input, parse_quote, Data, DataEnum, DeriveInput, Fields, Generics};
use syn::{parse_macro_input, parse_quote, Data, DataEnum, DeriveInput, Fields, LitInt, Generics};
use crate::get_disc_padding;
pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
@@ -10,7 +12,8 @@ pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
};
let name = &input.ident;
let ser_code = gen_ser_code_enum(data, &name, &input.generics);
let disc_padding = get_disc_padding(&input);
let ser_code = gen_ser_code_enum(data, &name, disc_padding, &input.generics);
let ser_impl = &mut input.generics.clone();
ser_impl.params.push(parse_quote!('__LIFETIME));
ser_impl.params.push(parse_quote!(__WRITER: ::std::io::Write));
@@ -48,7 +51,7 @@ fn gen_ser_code_fields(fields: &Fields) -> TokenStream {
}
}
fn gen_ser_code_enum(data: &DataEnum, name: &Ident, generics: &Generics) -> TokenStream {
fn gen_ser_code_enum(data: &DataEnum, name: &Ident, padding: Option<LitInt>, generics: &Generics) -> TokenStream {
let mut arms = vec![];
for f in &data.variants {
let ident = &f.ident;
@@ -56,10 +59,17 @@ fn gen_ser_code_enum(data: &DataEnum, name: &Ident, generics: &Generics) -> Toke
let expanded = quote! { #name::#ident #ser_fields };
arms.push(expanded);
}
let write_padding = match padding {
Some(x) => quote! {
let mut padding = [0; #x];
::std::io::Write::write_all(writer, &padding)?;
},
None => quote! { },
};
quote! {
let disc = unsafe { *(self as *const #name #generics as *const u32) };
::endio::LEWrite::write(writer, disc)?;
::endio::LEWrite::write(writer, 0u8)?;
#write_padding
match self {
#(#arms)*
}

View File

@@ -16,7 +16,7 @@ impl From<ClientMessage> for Message {
}
#[derive(Debug, FromVariants, ServiceMessageS)]
#[non_exhaustive]
#[disc_padding=1]
#[repr(u32)]
pub enum ClientMessage {
LoginResponse(LoginResponse),

View File

@@ -16,6 +16,7 @@ pub enum LuMessage {
}
#[derive(Debug, ServiceMessageD)]
#[disc_padding=1]
#[repr(u32)]
pub enum AuthMessage {
LoginRequest(LoginRequest)

View File

@@ -3,77 +3,24 @@ use std::io::Result as Res;
use endio::{Deserialize, LERead};
use endio::LittleEndian as LE;
use lu_packets_derive::ServiceMessageD;
use crate::common::{err, LuWStr33, ObjId};
use crate::common::{LuWStr33, ObjId};
enum ChatId {
GeneralChatMessage = 1,
PrivateChatMessage = 2,
AddFriendResponse = 8,
#[derive(Debug, ServiceMessageD)]
#[disc_padding=9]
#[repr(u32)]
pub enum ChatMessage {
GeneralChatMessage(GeneralChatMessage) = 1,
PrivateChatMessage(PrivateChatMessage) = 2,
AddFriendResponse(AddFriendResponse) = 8,
GetFriendsList = 10,
GetIgnoreList = 13,
TeamInviteResponse = 16,
TeamLeave = 18,
TeamInviteResponse(TeamInviteResponse) = 16,
TeamLeave(TeamLeave) = 18,
TeamGetStatus = 21,
RequestMinimumChatMode = 50,
RequestMinimumChatModePrivate = 51,
}
#[derive(Debug)]
pub enum ChatMessage {
GeneralChatMessage(GeneralChatMessage),
PrivateChatMessage(PrivateChatMessage),
AddFriendResponse(AddFriendResponse),
GetFriendsList,
GetIgnoreList,
TeamInviteResponse(TeamInviteResponse),
TeamLeave,
TeamGetStatus,
RequestMinimumChatMode(RequestMinimumChatMode),
RequestMinimumChatModePrivate(RequestMinimumChatModePrivate),
}
impl<R: Read+LERead> Deserialize<LE, R> for ChatMessage
where u8: Deserialize<LE, R>,
u32: Deserialize<LE, R>,
u64: Deserialize<LE, R>,
GeneralChatMessage: Deserialize<LE, R>,
PrivateChatMessage: Deserialize<LE, R>,
AddFriendResponse: Deserialize<LE, R>,
TeamInviteResponse: Deserialize<LE, R>,
RequestMinimumChatMode: Deserialize<LE, R>,
RequestMinimumChatModePrivate: Deserialize<LE, R> {
fn deserialize(reader: &mut R) -> Res<Self> {
let packet_id: u32 = LERead::read(reader)?;
let _padding: u8 = LERead::read(reader)?;
let routed_obj_id: u64 = LERead::read(reader)?;
assert_eq!(routed_obj_id, 0);
Ok(if packet_id == ChatId::GeneralChatMessage as u32 {
Self::GeneralChatMessage(LERead::read(reader)?)
} else if packet_id == ChatId::PrivateChatMessage as u32 {
Self::PrivateChatMessage(LERead::read(reader)?)
} else if packet_id == ChatId::AddFriendResponse as u32 {
Self::AddFriendResponse(LERead::read(reader)?)
} else if packet_id == ChatId::GetFriendsList as u32 {
Self::GetFriendsList
} else if packet_id == ChatId::GetIgnoreList as u32 {
Self::GetIgnoreList
} else if packet_id == ChatId::TeamGetStatus as u32 {
Self::TeamGetStatus
} else if packet_id == ChatId::TeamInviteResponse as u32 {
Self::TeamInviteResponse(LERead::read(reader)?)
} else if packet_id == ChatId::TeamLeave as u32 {
let mut unused = [0; 66];
Read::read(reader, &mut unused)?;
Self::TeamLeave
} else if packet_id == ChatId::RequestMinimumChatMode as u32 {
Self::RequestMinimumChatMode(LERead::read(reader)?)
} else if packet_id == ChatId::RequestMinimumChatModePrivate as u32 {
Self::RequestMinimumChatModePrivate(LERead::read(reader)?)
} else {
return err("chat id", packet_id);
})
}
RequestMinimumChatMode(RequestMinimumChatMode) = 50,
RequestMinimumChatModePrivate(RequestMinimumChatModePrivate) = 51,
}
#[derive(Debug)]
@@ -189,6 +136,11 @@ pub struct TeamInviteResponse {
pub sender: ObjId,
}
#[derive(Debug, Deserialize)]
pub struct TeamLeave {
pub unused: LuWStr33,
}
#[derive(Debug, Deserialize)]
pub struct RequestMinimumChatMode {
pub chat_channel: u8, // todo: separate type?

View File

@@ -28,7 +28,7 @@ impl<C> From<GeneralMessage> for Message<C> {
}
#[derive(Debug, ServiceMessageS)]
#[non_exhaustive]
#[disc_padding=1]
#[repr(u32)]
pub enum GeneralMessage {
Handshake(Handshake),

View File

@@ -7,6 +7,7 @@ use lu_packets_derive::ServiceMessageD;
use crate::common::ServiceId;
#[derive(Debug, ServiceMessageD)]
#[disc_padding=1]
#[repr(u32)]
pub enum GeneralMessage {
Handshake(Handshake)

View File

@@ -18,6 +18,7 @@ impl From<ClientMessage> for Message {
#[derive(Debug, FromVariants, ServiceMessageS)]
#[non_exhaustive]
#[disc_padding=1]
#[repr(u32)]
pub enum ClientMessage {
CharacterListResponse(CharacterListResponse) = 6,

View File

@@ -26,6 +26,7 @@ pub enum LuMessage {
}
#[derive(Debug, ServiceMessageD)]
#[disc_padding=1]
#[repr(u32)]
pub enum WorldMessage {
ClientValidation(ClientValidation) = 1,