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