fromsoftware_shared_macros/lib.rs
1use proc_macro::TokenStream;
2use quote::*;
3use syn::*;
4
5mod multi_param;
6
7mod for_all_subclasses;
8mod subclass;
9mod superclass;
10mod utils;
11
12/// Annotates a struct as a Dantelion2 singleton to be looked up using a single
13/// string argument.
14///
15/// This is only guaranteed to make the struct work with the
16/// `fromsoftware_shared::singleton::get_instance` function. Any other added
17/// functionality is considered an implementation detail and shouldn't be relied
18/// upon.
19#[proc_macro_attribute]
20pub fn singleton(args: TokenStream, input: TokenStream) -> TokenStream {
21 let input_struct: ItemStruct = parse_macro_input!(input as ItemStruct);
22 let input_struct_ident = input_struct.ident.clone();
23 let dlrf_name = parse_macro_input!(args as LitStr).value();
24
25 TokenStream::from(quote! {
26 #input_struct
27
28 impl ::fromsoftware_shared::FromSingleton for #input_struct_ident {
29 fn name() -> ::std::borrow::Cow<'static, str> {
30 ::std::borrow::Cow::Borrowed(#dlrf_name)
31 }
32 }
33 })
34}
35
36/// Annotates a trait to automatically generate getters and setters that forward
37/// to methods of the same name in various structs.
38///
39/// This is used to create traits that encapsulate state that's shared across
40/// multiple parameter definitions.
41///
42/// This trait takes as arguments the names of various structs for which it
43/// should automatically generate an implementation. It should annotate a trait
44/// that contains a `fields!` macro, using the same named field syntax that a
45/// struct uses. For each field, a getter and setter is generated both in the
46/// trait and in its implementation for each struct.
47///
48/// ```rs
49/// #[multi_param(EQUIP_PARAM_ACCESSORY_ST, EQUIP_PARAM_GOODS_ST)]
50/// pub trait EquipParamPassive: EquipParam {
51/// fields! {
52/// sfx_variation_id: i32,
53/// ref_category: u8,
54/// sp_effect_category: u8,
55/// shop_lv: i16,
56/// }
57/// }
58/// ```
59///
60/// ## Field Attributes
61///
62/// This can also be used as to annotate field attributes.
63///
64/// ### `rename()`
65///
66/// You can use the `rename()` annotation to rename the field this targets for a
67/// particular struct. This can be useful if the same logical field has
68/// different names, which sometimes happens due to typos in FromSoftware's
69/// internal names.
70///
71/// The syntax is `#[multi_param(rename(param = ..., name = ...))]`. The struct
72/// must exactly match one of the structs passed to the outer annotation, and
73/// the `name` must be a valid identifier.
74///
75/// ```rs
76/// #[multi_param(
77/// EQUIP_PARAM_ACCESSORY_ST,
78/// EQUIP_PARAM_GOODS_ST,
79/// EQUIP_PARAM_PROTECTOR_ST,
80/// EQUIP_PARAM_WEAPON_ST
81/// )]
82/// pub trait EquipParam {
83/// fields! {
84/// weight: f32,
85/// basic_price: i32,
86/// sell_value: i32,
87/// sort_id: i32,
88/// vagrant_item_lot_id: i32,
89/// #[multi_param(
90/// rename(param = EQUIP_PARAM_PROTECTOR_ST, name = "vagrant_bonusene_drop_item_lot_id"),
91/// rename(param = EQUIP_PARAM_WEAPON_ST, name = "vagrant_bonusene_drop_item_lot_id"),
92/// )]
93/// vagrant_bonus_ene_drop_item_lot_id: i32;
94/// vagrant_item_ene_drop_item_lot_id: i32,
95/// }
96/// }
97/// ```rs
98#[proc_macro_attribute]
99pub fn multi_param(args: TokenStream, input: TokenStream) -> TokenStream {
100 match multi_param::multi_param_helper(args, input) {
101 Ok(stream) => stream,
102 Err(err) => err.into_compile_error().into(),
103 }
104}
105
106/// A derive macro for `fromsoftware_shared::Subclass`.
107///
108/// ## Finding the RVA
109///
110/// This adds an implementation of `Subclass` that gets its VMT address from a
111/// standard RVA struct. This assumes:
112///
113/// * The crate using this contains a `crate::rva` module that exposes a `get()`
114/// function.
115///
116/// * The `get()` function's return value has a public field whose name is a
117/// snake-case version of this struct's name, followed by `_vmt`.
118///
119/// For example, `ChrIns` uses `crate::rva::get().chr_ins_vmt` as its VMT RVA.
120///
121/// ## Determining the Superclass
122///
123/// By default, the type of the first field in the subclass is used as the
124/// superclass. You can explicitly choose one or more superclasses instead using
125/// the `#[subclass(base = SuperclassType)]` attribute on the struct.
126///
127/// ## Additional Features
128///
129/// This macro will also add trait implementations for `AsRef<SuperclassType>`,
130/// `AsMut<SuperclassType>`, and `TryFrom<&SuperclassType>`.
131///
132/// It will also implement `Deref<Target = SuperclassType>` and `DerefMut`, but
133/// because a type can only have one implementation of these traits, if this
134/// declares multiple superclasses they will only be implemented for the first
135/// one. Since types can be transitively dereferenced, be sure to order the
136/// bottommost superclass first so that all superclass methods can be accessed.
137///
138/// ## Safety
139///
140/// The `fromsoftware_shared::Subclass` trait is unsafe, and even though there's
141/// currently no way to require that a derive macro be explicitly flagged as
142/// unsafe, this does not add any additional safety guarantees beyond a manual
143/// implementation. PLease read the `Subclass` documentation carefully to
144/// understand the requirements to use this safety.
145#[proc_macro_derive(Subclass, attributes(subclass))]
146pub fn derive_subclass(input: TokenStream) -> TokenStream {
147 match subclass::subclass_helper(input) {
148 Ok(stream) => stream,
149 Err(err) => err.into_compile_error().into(),
150 }
151}
152
153/// A derive macro for `fromsoftware_shared::Superclass`.
154///
155/// ## Finding the RVA
156///
157/// This adds an implementation of `Subclass` that gets its VMT address from a
158/// standard RVA struct. This assumes:
159///
160/// * The crate using this contains a `crate::rva` module that exposes a `get()`
161/// function.
162///
163/// * The `get()` function's return value has a public field whose name is a
164/// snake-case version of this struct's name, followed by `_vmt`.
165///
166/// For example, `ChrIns` uses `crate::rva::get().chr_ins_vmt` as its VMT RVA.
167///
168/// ## Subclass Enums
169///
170/// By default, this macro will just generate a straightforward implementation
171/// of `Superclass`. But if you want, you can add a
172/// `#[superclass(children(ChildName1, ChildName2))]` attribute to the struct.
173/// If you do, the macro will also define two enums, one immutable and one
174/// mutable.
175///
176/// For example:
177///
178/// ```rs
179/// #[repr(C)]
180/// #[derive(Superclass)]
181/// #[superclass(children(Cow, Pig))]
182/// pub struct Animal {
183/// _vftable: usize,
184/// }
185/// ```
186///
187/// will generate
188///
189/// ```rs
190/// pub enum AnimalSubclasses<'sub> {
191/// Cow(&'sub Cow),
192/// Pig(&'sub Pig),
193/// Animal(&'sub Animal),
194/// }
195///
196/// pub enum AnimalSubclassesMut<'sub> {
197/// Cow(&'sub mut Cow),
198/// Pig(&'sub mut Pig),
199/// Animal(&'sub mut Animal),
200/// }
201///
202/// impl AnimalSubclasses<'_> {
203/// pub fn superclass(&self) -> &Animal;
204/// }
205///
206/// impl AnimalSubclassesMut<'_> {
207/// pub fn superclass(&self) -> &Animal;
208/// pub fn superclass_mut(&mut self) -> &mut Animal;
209/// }
210///
211/// impl<'sub> From<AnimalSubclassesMut<'sub>> for AnimalSubclasses<'sub> {}
212/// impl<'sub> From<&'sub T> for AnimalSubclasses<'sub> where T: Subclass<Animal> {}
213/// impl<'sub> From<&'sub mut T> for AnimalSubclassesMut<'sub> where T: Subclass<Animal> {}
214/// ```
215///
216/// ## Safety
217///
218/// The `fromsoftware_shared::Superclass` trait is unsafe, and even though
219/// there's currently no way to require that a derive macro be explicitly
220/// flagged as unsafe, this does not add any additional safety guarantees beyond
221/// a manual implementation. PLease read the `Superclass` documentation
222/// carefully to understand the requirements to use this safety.
223#[proc_macro_derive(Superclass, attributes(superclass))]
224pub fn derive_superclass(input: TokenStream) -> TokenStream {
225 match superclass::superclass_helper(input) {
226 Ok(stream) => stream,
227 Err(err) => err.into_compile_error().into(),
228 }
229}
230
231/// A proc macro attribute for defining an extension trait that makes a set of
232/// methods available for all subclasses of a superclass.
233///
234/// This expects to be used on a trait impl whose trait name is **not defined**
235/// and whose target is `Subclass<...>`. This impl should include functions.
236/// Unlike normal trait implementations, this impl **should have a visibility
237/// modifier** (unless you want it to be private). For example:
238///
239/// ```rs
240/// #[for_all_subclasses]
241/// pub impl ChrInsExt for Subclass<ChrIns> {
242/// pub fn apply_speffect(&mut self, sp_effect: i32, dont_sync: bool) {
243/// let rva = Program::current()
244/// .rva_to_va(rva::get().chr_ins_apply_speffect)
245/// .unwrap();
246///
247/// let call = unsafe { transmute::<u64, extern "C" fn(&mut ChrIns, i32, bool) -> u64>(rva) };
248/// call(self, sp_effect, dont_sync);
249/// }
250///
251/// pub fn remove_speffect(&mut self, sp_effect: i32) {
252/// let rva = Program::current()
253/// .rva_to_va(rva::get().chr_ins_remove_speffect)
254/// .unwrap();
255///
256/// let call = unsafe { transmute::<u64, extern "C" fn(&mut ChrIns, i32) -> u64>(rva) };
257/// call(self, sp_effect);
258/// }
259/// }
260/// ```
261///
262/// This will define a trait with the given name and visibility, then implement
263/// it for all subclasses of the given superclass. This allow superclass methods
264/// to be freely called on any subclass, delegating to the superclass they
265/// contain.
266#[proc_macro_attribute]
267pub fn for_all_subclasses(_args: TokenStream, input: TokenStream) -> TokenStream {
268 match for_all_subclasses::for_all_subclasses_helper(input) {
269 Ok(stream) => stream,
270 Err(err) => err.into_compile_error().into(),
271 }
272}