Skip to main content

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///
99/// ## Enums
100///
101/// Each trait also generates two enums, one mutable and one immutable, which
102/// can be used in contexts where it's relevant *which* param type in particular
103/// you have. These enums have a variant for each defined parameter type. The
104/// above example would define:
105///
106/// ```rs
107/// pub enum EquipParamStruct<'a> {
108///   EQUIP_PARAM_ACCESSORY_ST(&'a EQUIP_PARAM_ACCESSORY_ST),
109///   EQUIP_PARAM_GOODS_ST(&'a EQUIP_PARAM_GOODS_ST),
110///   EQUIP_PARAM_PROTECTOR_ST(&'a EQUIP_PARAM_PROTECTOR_ST),
111///   EQUIP_PARAM_WEAPON_ST(&'a EQUIP_PARAM_WEAPON_ST),
112/// }
113///
114/// impl EquipParamStruct<'_> {
115///   pub fn as_dyn(&self) -> &dyn EquipParam;
116/// }
117///
118/// pub enum EquipParamStructMut<'a> {
119///   EQUIP_PARAM_ACCESSORY_ST(&'a mut EQUIP_PARAM_ACCESSORY_ST),
120///   EQUIP_PARAM_GOODS_ST(&'a mut EQUIP_PARAM_GOODS_ST),
121///   EQUIP_PARAM_PROTECTOR_ST(&'a mut EQUIP_PARAM_PROTECTOR_ST),
122///   EQUIP_PARAM_WEAPON_ST(&'a mut EQUIP_PARAM_WEAPON_ST),
123/// }
124///
125/// impl EquipParamStructMut<'_> {
126///   pub fn as_dyn(&mut self) -> &mut dyn EquipParam;
127/// }
128/// ```
129///
130/// You can access these using the `as_enum()` and `as_enum_mut()` methods.
131#[proc_macro_attribute]
132pub fn multi_param(args: TokenStream, input: TokenStream) -> TokenStream {
133    match multi_param::multi_param_helper(args, input) {
134        Ok(stream) => stream,
135        Err(err) => err.into_compile_error().into(),
136    }
137}
138
139/// A derive macro for `fromsoftware_shared::Subclass`.
140///
141/// ## Finding the RVA
142///
143/// This adds an implementation of `Subclass` that gets its VMT address from a
144/// standard RVA struct. This assumes:
145///
146/// * The crate using this contains a `crate::rva` module that exposes a `get()`
147///   function.
148///
149/// * The `get()` function's return value has a public field whose name is a
150///   snake-case version of this struct's name, followed by `_vmt`.
151///
152/// For example, `ChrIns` uses `crate::rva::get().chr_ins_vmt` as its VMT RVA.
153///
154/// ## Determining the Superclass
155///
156/// By default, the type of the first field in the subclass is used as the
157/// superclass. You can explicitly choose one or more superclasses instead using
158/// the `#[subclass(base = SuperclassType)]` attribute on the struct.
159///
160/// ## Additional Features
161///
162/// This macro will also add trait implementations for `AsRef<SuperclassType>`,
163/// `AsMut<SuperclassType>`, and `TryFrom<&SuperclassType>`.
164///
165/// It will also implement `Deref<Target = SuperclassType>` and `DerefMut`, but
166/// because a type can only have one implementation of these traits, if this
167/// declares multiple superclasses they will only be implemented for the first
168/// one. Since types can be transitively dereferenced, be sure to order the
169/// bottommost superclass first so that all superclass methods can be accessed.
170///
171/// ## Safety
172///
173/// The `fromsoftware_shared::Subclass` trait is unsafe, and even though there's
174/// currently no way to require that a derive macro be explicitly flagged as
175/// unsafe, this does not add any additional safety guarantees beyond a manual
176/// implementation. PLease read the `Subclass` documentation carefully to
177/// understand the requirements to use this safety.
178#[proc_macro_derive(Subclass, attributes(subclass))]
179pub fn derive_subclass(input: TokenStream) -> TokenStream {
180    match subclass::subclass_helper(input) {
181        Ok(stream) => stream,
182        Err(err) => err.into_compile_error().into(),
183    }
184}
185
186/// A derive macro for `fromsoftware_shared::Superclass`.
187///
188/// ## Finding the RVA
189///
190/// This adds an implementation of `Subclass` that gets its VMT address from a
191/// standard RVA struct. This assumes:
192///
193/// * The crate using this contains a `crate::rva` module that exposes a `get()`
194///   function.
195///
196/// * The `get()` function's return value has a public field whose name is a
197///   snake-case version of this struct's name, followed by `_vmt`.
198///
199/// For example, `ChrIns` uses `crate::rva::get().chr_ins_vmt` as its VMT RVA.
200///
201/// ## Subclass Enums
202///
203/// By default, this macro will just generate a straightforward implementation
204/// of `Superclass`. But if you want, you can add a
205/// `#[superclass(children(ChildName1, ChildName2))]` attribute to the struct.
206/// If you do, the macro will also define two enums, one immutable and one
207/// mutable.
208///
209/// For example:
210///
211/// ```rs
212/// #[repr(C)]
213/// #[derive(Superclass)]
214/// #[superclass(children(Cow, Pig))]
215/// pub struct Animal {
216///   _vftable: usize,
217/// }
218/// ```
219///
220/// will generate
221///
222/// ```rs
223/// pub enum AnimalSubclasses<'sub> {
224///   Cow(&'sub Cow),
225///   Pig(&'sub Pig),
226///   Animal(&'sub Animal),
227/// }
228///
229/// pub enum AnimalSubclassesMut<'sub> {
230///   Cow(&'sub mut Cow),
231///   Pig(&'sub mut Pig),
232///   Animal(&'sub mut Animal),
233/// }
234///
235/// impl AnimalSubclasses<'_> {
236///   pub fn superclass(&self) -> &Animal;
237/// }
238///
239/// impl AnimalSubclassesMut<'_> {
240///   pub fn superclass(&self) -> &Animal;
241///   pub fn superclass_mut(&mut self) -> &mut Animal;
242/// }
243///
244/// impl<'sub> From<AnimalSubclassesMut<'sub>> for AnimalSubclasses<'sub> {}
245/// impl<'sub> From<&'sub T> for AnimalSubclasses<'sub> where T: Subclass<Animal> {}
246/// impl<'sub> From<&'sub mut T> for AnimalSubclassesMut<'sub> where T: Subclass<Animal> {}
247/// ```
248///
249/// ## Safety
250///
251/// The `fromsoftware_shared::Superclass` trait is unsafe, and even though
252/// there's currently no way to require that a derive macro be explicitly
253/// flagged as unsafe, this does not add any additional safety guarantees beyond
254/// a manual implementation. PLease read the `Superclass` documentation
255/// carefully to understand the requirements to use this safety.
256#[proc_macro_derive(Superclass, attributes(superclass))]
257pub fn derive_superclass(input: TokenStream) -> TokenStream {
258    match superclass::superclass_helper(input) {
259        Ok(stream) => stream,
260        Err(err) => err.into_compile_error().into(),
261    }
262}
263
264/// A proc macro attribute for defining an extension trait that makes a set of
265/// methods available for all subclasses of a superclass.
266///
267/// This expects to be used on a trait impl whose trait name is **not defined**
268/// and whose target is `Subclass<...>`. This impl should include functions.
269/// Unlike normal trait implementations, this impl **should have a visibility
270/// modifier** (unless you want it to be private). For example:
271///
272/// ```rs
273/// #[for_all_subclasses]
274/// pub impl ChrInsExt for Subclass<ChrIns> {
275///     fn apply_speffect(&mut self, sp_effect: i32, dont_sync: bool) {
276///         let rva = Program::current()
277///             .rva_to_va(rva::get().chr_ins_apply_speffect)
278///             .unwrap();
279///
280///         let call = unsafe { transmute::<u64, extern "C" fn(&mut ChrIns, i32, bool) -> u64>(rva) };
281///         call(self, sp_effect, dont_sync);
282///     }
283///
284///     fn remove_speffect(&mut self, sp_effect: i32) {
285///         let rva = Program::current()
286///             .rva_to_va(rva::get().chr_ins_remove_speffect)
287///             .unwrap();
288///
289///         let call = unsafe { transmute::<u64, extern "C" fn(&mut ChrIns, i32) -> u64>(rva) };
290///         call(self, sp_effect);
291///     }
292/// }
293/// ```
294///
295/// This will define a trait with the given name and visibility, then implement
296/// it for all subclasses of the given superclass. This allow superclass methods
297/// to be freely called on any subclass, delegating to the superclass they
298/// contain.
299#[proc_macro_attribute]
300pub fn for_all_subclasses(_args: TokenStream, input: TokenStream) -> TokenStream {
301    match for_all_subclasses::for_all_subclasses_helper(input) {
302        Ok(stream) => stream,
303        Err(err) => err.into_compile_error().into(),
304    }
305}