1mod parse;
2
3use proc_macro2::{Span, TokenStream};
4use quote::{quote, quote_spanned};
5use syn::{Expr, LitStr};
6
7#[proc_macro]
12pub fn arcdps_export(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
13 let input = syn::parse_macro_input!(item as parse::ArcDpsGen);
14 let sig = input.sig;
15 let build = std::env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION is not set") + "\0";
16 let build = LitStr::new(build.as_str(), Span::call_site());
17 let (raw_name, span) = if let Some(input_name) = input.name {
18 let name = input_name.value();
19 (name, input_name.span())
20 } else {
21 let name = std::env::var("CARGO_PKG_NAME").expect("CARGO_PKG_NAME is not set");
22 (name, Span::call_site())
23 };
24 let name = LitStr::new(raw_name.as_str(), span);
25 let out_name = raw_name + "\0";
26 let out_name = LitStr::new(out_name.as_str(), span);
27
28 let (abstract_combat, cb_combat) = build_combat(input.raw_combat, input.combat);
29 let (abstract_combat_local, cb_combat_local) =
30 build_combat_local(input.raw_combat_local, input.combat_local);
31 let (abstract_imgui, cb_imgui) = build_imgui(input.raw_imgui, input.imgui);
32 let (abstract_options_end, cb_options_end) =
33 build_options_end(input.raw_options_end, input.options_end);
34 let (abstract_options_windows, cb_options_windows) =
35 build_options_windows(input.raw_options_windows, input.options_windows);
36 let (abstract_wnd_filter, cb_wnd_filter) =
37 build_wnd_filter(input.raw_wnd_filter, input.wnd_filter);
38 let (abstract_wnd_nofilter, cb_wnd_nofilter) =
39 build_wnd_nofilter(input.raw_wnd_nofilter, input.wnd_nofilter);
40
41 let export = quote! {
42 ArcDpsExport {
43 size: ::std::mem::size_of::<ArcDpsExport>(),
44 sig: #sig,
45 imgui_version: 18000,
46 out_build: #build.as_ptr(),
47 out_name: #out_name.as_ptr(),
48 combat: #cb_combat,
49 combat_local: #cb_combat_local,
50 imgui: #cb_imgui,
51 options_end: #cb_options_end,
52 options_windows: #cb_options_windows,
53 wnd_filter: #cb_wnd_filter,
54 wnd_nofilter: #cb_wnd_nofilter,
55 }
56 };
57
58 let init = if let Some(init) = input.init {
59 let span = syn::Error::new_spanned(&init, "").span();
60 quote_spanned! (span => unsafe { (#init as InitFunc)(__SWAPCHAIN) })
61 } else {
62 quote! {Ok(())}
63 };
64
65 let release = if let Some(release) = input.release {
66 let span = syn::Error::new_spanned(&release, "").span();
67 quote_spanned! (span => (#release as ReleaseFunc)())
68 } else {
69 quote! {}
70 };
71
72 let (abstract_extras_squad_update, extras_squad_update) = build_extras_squad_update(
73 input.raw_unofficial_extras_squad_update,
74 input.unofficial_extras_squad_update,
75 );
76 let (abstract_extras_chat_message, extras_chat_message) = build_extras_chat_message(
77 input.raw_unofficial_extras_chat_message,
78 input.unofficial_extras_chat_message,
79 );
80 let (abstract_extras_chat_message2, extras_chat_message2) = build_extras_chat_message2(
81 input.raw_unofficial_extras_chat_message2,
82 input.unofficial_extras_chat_message2,
83 );
84 let abstract_extras_init = build_extras_init(
85 input.raw_unofficial_extras_init,
86 input.unofficial_extras_init,
87 extras_squad_update,
88 extras_chat_message,
89 extras_chat_message2,
90 &out_name,
91 );
92
93 #[cfg(feature = "imgui")]
94 let sys_init = quote! {
95 use ::arcdps::imgui;
96
97 #[no_mangle]
98 pub unsafe extern "system" fn get_init_addr(
101 __arc_version: *mut c_char,
102 __imguictx: *mut imgui::sys::ImGuiContext,
103 __id3dptr: *mut c_void,
104 __arc_dll: *mut c_void,
105 __mallocfn: Option<unsafe extern "C" fn(sz: usize, user_data: *mut c_void) -> *mut c_void>,
106 __freefn: Option<unsafe extern "C" fn(ptr: *mut c_void, user_data: *mut c_void)>,
107 ) -> unsafe extern "system" fn() -> *const ArcDpsExport {
108 imgui::sys::igSetCurrentContext(__imguictx);
109 imgui::sys::igSetAllocatorFunctions(__mallocfn, __freefn, ::core::ptr::null_mut());
110 __CTX = Some(imgui::Context::current());
111 let __ctx = &raw const __CTX;
112 __UI = Some(imgui::Ui::from_ctx((*__ctx).as_ref().unwrap()));
113 __SWAPCHAIN = NonNull::new(__id3dptr);
114 ::arcdps::__init(__arc_version, __arc_dll, #name);
115 __load
116 }
117
118 static mut __CTX: Option<imgui::Context> = None;
119 static mut __UI: Option<imgui::Ui> = None;
120 };
121
122 #[cfg(not(feature = "imgui"))]
123 let sys_init = quote! {
124 #[no_mangle]
125 pub unsafe extern "system" fn get_init_addr(
128 __arc_version: *mut c_char,
129 _imguictx: *mut c_void,
130 __id3dptr: *mut c_void,
131 __arc_dll: *mut c_void,
132 _mallocfn: Option<unsafe extern "C" fn(sz: usize, user_data: *mut c_void) -> *mut c_void>,
133 _freefn: Option<unsafe extern "C" fn(ptr: *mut c_void, user_data: *mut c_void)>,
134 ) -> unsafe extern "system" fn() -> *const ArcDpsExport {
135 __SWAPCHAIN = NonNull::new(__id3dptr);
136 ::arcdps::__init(__arc_version, __arc_dll, #name);
137 __load
138 }
139 };
140
141 let res = quote! {
142 mod __arcdps_gen_export {
143 use super::*;
144 use ::std::os::raw::{c_char, c_void};
145 use ::std::ptr::NonNull;
146 use ::arcdps::ArcDpsExport;
147 use ::arcdps::{InitFunc, ReleaseFunc};
148
149 #abstract_combat
150 #abstract_combat_local
151 #abstract_imgui
152 #abstract_options_end
153 #abstract_options_windows
154 #abstract_wnd_filter
155 #abstract_wnd_nofilter
156 #abstract_extras_squad_update
157 #abstract_extras_chat_message
158 #abstract_extras_chat_message2
159 #abstract_extras_init
160
161 static __EXPORT: ArcDpsExport = #export;
162 static mut __EXPORT_ERROR: ArcDpsExport = ArcDpsExport {
163 size: 0,
164 sig: 0,
165 imgui_version: 18000,
166 out_build: #build.as_ptr(),
167 out_name: #out_name.as_ptr(),
168 combat: None,
169 combat_local: None,
170 imgui: None,
171 options_end: None,
172 options_windows: None,
173 wnd_filter: None,
174 wnd_nofilter: None,
175 };
176 static mut __ERROR_STRING: String = String::new();
177 static mut __SWAPCHAIN: Option<NonNull<c_void>> = None;
178
179 unsafe extern "system" fn __load() -> *const ArcDpsExport {
180 let mut __export = &raw const __EXPORT;
181 let __res: Result<(), Box<dyn ::std::error::Error>> = #init;
182 if let Err(__e) = __res {
183 unsafe {
184 __ERROR_STRING = __e.to_string() + "\0";
185 __EXPORT_ERROR.size = &raw const __ERROR_STRING as _;
186 __export = &raw const __EXPORT_ERROR;
187 }
188 }
189
190 __export
191 }
192
193 unsafe extern "system" fn __unload() {
194 #release
195 }
196
197 #sys_init
198
199 #[no_mangle]
200 pub extern "system" fn get_release_addr() -> unsafe extern "system" fn() {
202 __unload
203 }
204 }
205 };
206 res.into()
207}
208
209fn build_extras_squad_update(
210 raw: Option<Expr>,
211 safe: Option<Expr>,
212) -> (TokenStream, Option<TokenStream>) {
213 let mut abstract_wrapper = quote! {};
214 let cb_safe = match (raw, safe) {
215 (Some(raw), _) => {
216 let span = syn::Error::new_spanned(&raw, "").span();
217 Some(quote_spanned!(span => Some(#raw as _) ))
218 }
219 (_, Some(safe)) => {
220 let span = syn::Error::new_spanned(&safe, "").span();
221 abstract_wrapper = quote_spanned!(span =>
222 unsafe extern "C" fn __abstract_extras_squad_update(__users: *const ::arcdps::RawUserInfo, __count: u64) {
223 let _ = #safe as ::arcdps::ExtrasSquadUpdateCallback;
224 let __users = ::std::slice::from_raw_parts(__users, __count as _);
225 let __users = __users.iter().map(::arcdps::helpers::convert_extras_user as ::arcdps::UserConvert);
226 #safe(__users)
227 });
228 Some(
229 quote_spanned!(span => Some(__arcdps_gen_export::__abstract_extras_squad_update as _) ),
230 )
231 }
232 _ => None,
233 };
234 (abstract_wrapper, cb_safe)
235}
236
237fn build_extras_chat_message(
238 raw: Option<Expr>,
239 safe: Option<Expr>,
240) -> (TokenStream, Option<TokenStream>) {
241 let mut abstract_wrapper = quote! {};
242 let cb_safe = match (raw, safe) {
243 (Some(raw), _) => {
244 let span = syn::Error::new_spanned(&raw, "").span();
245 Some(quote_spanned!(span => Some(#raw as _) ))
246 }
247 (_, Some(safe)) => {
248 let span = syn::Error::new_spanned(&safe, "").span();
249 abstract_wrapper = quote_spanned!(span =>
250 unsafe extern "C" fn __abstract_extras_chat_message(__msg: *const ::arcdps::RawSquadMessageInfo) {
251 let _ = #safe as ::arcdps::ExtrasChatMessageCallback;
252 let __msg = ::arcdps::helpers::convert_extras_squad_chat_message(&*__msg);
253 #safe(&__msg)
254 });
255 Some(
256 quote_spanned!(span => Some(__arcdps_gen_export::__abstract_extras_chat_message as _) ),
257 )
258 }
259 _ => None,
260 };
261 (abstract_wrapper, cb_safe)
262}
263
264fn build_extras_chat_message2(
265 raw: Option<Expr>,
266 safe: Option<Expr>,
267) -> (TokenStream, Option<TokenStream>) {
268 let mut abstract_wrapper = quote! {};
269 let cb_safe = match (raw, safe) {
270 (Some(raw), _) => {
271 let span = syn::Error::new_spanned(&raw, "").span();
272 Some(quote_spanned!(span => Some(#raw as _) ))
273 }
274 (_, Some(safe)) => {
275 let span = syn::Error::new_spanned(&safe, "").span();
276 abstract_wrapper = quote_spanned!(span =>
277 unsafe extern "C" fn __abstract_extras_chat_message2(__msg_type: ::arcdps::ChatMessageType, __msg: ::arcdps::RawChatMessageInfo2) {
278 let _ = #safe as ::arcdps::ExtrasChatMessage2Callback;
279 let __msg = ::arcdps::helpers::convert_extras_chat_message2(__msg_type, __msg);
280 #safe(&__msg)
281 });
282 Some(
283 quote_spanned!(span => Some(__arcdps_gen_export::__abstract_extras_chat_message2 as _) ),
284 )
285 }
286 _ => None,
287 };
288 (abstract_wrapper, cb_safe)
289}
290
291fn build_extras_init(
292 raw: Option<Expr>,
293 safe: Option<Expr>,
294 squad_update: Option<TokenStream>,
295 chat_message: Option<TokenStream>,
296 chat_message2: Option<TokenStream>,
297 name: &LitStr,
298) -> TokenStream {
299 let needs_init = squad_update.is_some() || chat_message.is_some();
300 let squad_cb = squad_update.unwrap_or(quote! { None });
301 let chat_cb = chat_message.unwrap_or(quote! { None });
302 let chat_cb2 = chat_message2.unwrap_or(quote! { None });
303
304 let basic_init = quote!(
305 if __addon.api_version != 2 {
306 return;
307 }
308 if __addon.max_info_version < 1 {
309 return;
310 }
311
312 fn __fill_v1(__sub: *mut ::arcdps::RawExtrasSubscriberInfo<::arcdps::InfoV1>) {
313 let __sub = unsafe { &mut *__sub };
314 __sub.header.info_version = 1;
315
316 __sub.subscriber_name = #name.as_ptr();
317 __sub.squad_update_callback = #squad_cb;
318 __sub.language_changed_callback = None;
319 __sub.key_bind_changed_callback = None;
320 }
321
322 fn __fill_v2(__sub: *mut ::arcdps::RawExtrasSubscriberInfo<::arcdps::InfoV2>) {
323 __fill_v1(__sub.cast());
324
325 let __sub = unsafe { &mut *__sub };
326 __sub.header.info_version = 2;
327
328 __sub.chat_message_callback = #chat_cb;
329 }
330
331 fn __fill_v3(__sub: *mut ::arcdps::RawExtrasSubscriberInfo<::arcdps::InfoV3>) {
332 __fill_v2(__sub.cast());
333
334 let __sub = unsafe { &mut *__sub };
335 __sub.header.info_version = 3;
336
337 __sub.chat_message_callback2 = #chat_cb2;
338 }
339
340 match __addon.max_info_version {
341 1 => __fill_v1(__sub.cast()),
342 2 => __fill_v2(__sub.cast()),
343 _ => __fill_v3(__sub.cast()),
344 }
345 );
346
347 let abstract_wrapper = match (raw, safe) {
348 (Some(raw), _) => {
349 let span = syn::Error::new_spanned(&raw, "").span();
350 quote_spanned!(span =>
351 let _ = #raw as ::arcdps::RawExtrasSubscriberInitSignature;
352
353 #raw(__addon, __sub)
354 )
355 }
356 (_, Some(safe)) => {
357 let span = syn::Error::new_spanned(&safe, "").span();
358 quote_spanned!(span =>
359 #basic_init
360
361 let _ = #safe as ::arcdps::ExtrasInitFunc;
362 let __user = ::arcdps::helpers::get_str_from_pc_char(__addon.self_account_name as _)
363 .map(|n| n.trim_start_matches(':'));
364 let __version = ::arcdps::helpers::get_str_from_pc_char(__addon.string_version as _);
365
366 #safe(__user, __version)
367 )
368 }
369 _ if needs_init => basic_init,
370 _ => return quote! {},
371 };
372 use syn::spanned::Spanned;
373 quote_spanned!(abstract_wrapper.span() =>
374 #[no_mangle]
375 unsafe extern "system" fn arcdps_unofficial_extras_subscriber_init(
376 __addon: &::arcdps::RawExtrasAddonInfo,
377 __sub: *mut ::arcdps::RawExtrasSubscriberInfoHeader
378 ) {
379 #abstract_wrapper
380 }
381 )
382}
383
384fn build_wnd_filter(raw_wnd: Option<Expr>, wnd: Option<Expr>) -> (TokenStream, TokenStream) {
385 build_wnd(raw_wnd, wnd, quote! { __abstract_wnd_filter })
386}
387
388fn build_wnd_nofilter(raw_wnd: Option<Expr>, wnd: Option<Expr>) -> (TokenStream, TokenStream) {
389 build_wnd(raw_wnd, wnd, quote! { __abstract_wnd_nofilter })
390}
391
392fn build_wnd(
393 raw_wnd_filter: Option<Expr>,
394 wnd_filter: Option<Expr>,
395 func_name: TokenStream,
396) -> (TokenStream, TokenStream) {
397 let mut abstract_wnd_filter = quote! {};
398 let cb_wnd_filter = match (raw_wnd_filter, wnd_filter) {
399 (Some(raw), _) => {
400 let span = syn::Error::new_spanned(&raw, "").span();
401 quote_spanned!(span => Some(#raw as _) )
402 }
403 (_, Some(safe)) => {
404 let span = syn::Error::new_spanned(&safe, "").span();
405 abstract_wnd_filter = quote_spanned!(span =>
406 unsafe extern "C" fn #func_name (_h_wnd: *mut c_void, __u_msg: u32,
407 __w_param: usize, __l_param: isize
408 ) -> u32 {
409 let _ = #safe as ::arcdps::WndProcCallback;
410 use ::arcdps::{WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWN, WM_SYSKEYUP};
411 match __u_msg {
412 WM_KEYDOWN | WM_KEYUP | WM_SYSKEYDOWN | WM_SYSKEYUP => {
413 let __key_down = __u_msg & 1 == 0;
414 let __prev_key_down = (__l_param >> 30) & 1 == 1;
415
416 if #safe(__w_param, __key_down, __prev_key_down)
417 {
418 __u_msg
419 } else {
420 0
421 }
422 },
423 _ => __u_msg,
424 }
425 });
426 quote_spanned!(span => Some(__arcdps_gen_export::#func_name as _) )
427 }
428 _ => quote! { None },
429 };
430 (abstract_wnd_filter, cb_wnd_filter)
431}
432
433fn build_options_windows(
434 raw_options_windows: Option<Expr>,
435 options_windows: Option<Expr>,
436) -> (TokenStream, TokenStream) {
437 let mut abstract_options_windows = quote! {};
438 let cb_options_windows = match (raw_options_windows, options_windows) {
439 (Some(raw), _) => {
440 let span = syn::Error::new_spanned(&raw, "").span();
441 quote_spanned!(span => Some(#raw as _) )
442 }
443 (_, Some(safe)) => {
444 let span = syn::Error::new_spanned(&safe, "").span();
445 abstract_options_windows = quote_spanned!(span =>
446 unsafe extern "C" fn __abstract_options_windows(__window_name: *mut c_char) -> bool {
447 let _ = #safe as ::arcdps::OptionsWindowsCallback;
448 let __ui = &raw const __UI;
449 let __ui = (*__ui).as_ref().unwrap();
450 #safe(__ui, ::arcdps::helpers::get_str_from_pc_char(__window_name))
451 });
452 quote_spanned!(span => Some(__arcdps_gen_export::__abstract_options_windows as _) )
453 }
454 _ => quote! { None },
455 };
456 (abstract_options_windows, cb_options_windows)
457}
458
459fn build_options_end(
460 raw_options_end: Option<Expr>,
461 options_end: Option<Expr>,
462) -> (TokenStream, TokenStream) {
463 let mut abstract_options_end = quote! {};
464 let cb_options_end = match (raw_options_end, options_end) {
465 (Some(raw), _) => {
466 let span = syn::Error::new_spanned(&raw, "").span();
467 quote_spanned!(span => Some(#raw as _) )
468 }
469 (_, Some(safe)) => {
470 let span = syn::Error::new_spanned(&safe, "").span();
471 abstract_options_end = quote_spanned!(span =>
472 unsafe extern "C" fn __abstract_options_end() {
473 let _ = #safe as ::arcdps::OptionsCallback;
474 let __ui = &raw const __UI;
475 let __ui = (*__ui).as_ref().unwrap();
476 #safe(__ui)
477 });
478 quote_spanned!(span => Some(__arcdps_gen_export::__abstract_options_end as _) )
479 }
480 _ => quote! { None },
481 };
482 (abstract_options_end, cb_options_end)
483}
484
485fn build_imgui(raw_imgui: Option<Expr>, imgui: Option<Expr>) -> (TokenStream, TokenStream) {
486 let mut abstract_imgui = quote! {};
487 let cb_imgui = match (raw_imgui, imgui) {
488 (Some(raw), _) => {
489 let span = syn::Error::new_spanned(&raw, "").span();
490 quote_spanned!(span => Some(#raw as _) )
491 }
492 (_, Some(safe)) => {
493 let span = syn::Error::new_spanned(&safe, "").span();
494 abstract_imgui = quote_spanned!(span =>
495 unsafe extern "C" fn __abstract_imgui(__loading: u32) {
496 let _ = #safe as ::arcdps::ImguiCallback;
497 let __ui = &raw const __UI;
498 let __ui = (*__ui).as_ref().unwrap();
499 #safe(__ui, __loading != 0)
500 });
501 quote_spanned!(span => Some(__arcdps_gen_export::__abstract_imgui as _) )
502 }
503 _ => quote! { None },
504 };
505 (abstract_imgui, cb_imgui)
506}
507
508fn build_combat_local(
509 raw_combat: Option<Expr>,
510 combat: Option<Expr>,
511) -> (TokenStream, TokenStream) {
512 build_cbt(raw_combat, combat, quote! { __abstract_combat_local })
513}
514
515fn build_combat(raw_combat: Option<Expr>, combat: Option<Expr>) -> (TokenStream, TokenStream) {
516 build_cbt(raw_combat, combat, quote! { __abstract_combat })
517}
518
519fn build_cbt(
520 raw_combat: Option<Expr>,
521 combat: Option<Expr>,
522 func_name: TokenStream,
523) -> (TokenStream, TokenStream) {
524 let mut abstract_combat = quote! {};
525 let cb_combat = match (raw_combat, combat) {
526 (Some(raw), _) => {
527 let span = syn::Error::new_spanned(&raw, "").span();
528 quote_spanned!(span => Some(#raw as _) )
529 }
530 (_, Some(safe)) => {
531 let span = syn::Error::new_spanned(&safe, "").span();
532 abstract_combat = quote_spanned!(span =>
533 unsafe extern "C" fn #func_name(
534 __ev: Option<&::arcdps::CombatEvent>,
535 __src: Option<&::arcdps::RawAgent>,
536 __dst: Option<&::arcdps::RawAgent>,
537 __skill_name: *mut c_char,
538 __id: u64,
539 __revision: u64,
540 ) {
541 let _ = #safe as ::arcdps::CombatCallback;
542 let __args = ::arcdps::helpers::get_combat_args_from_raw(__ev, __src, __dst, __skill_name);
543 #safe(__args.ev, __args.src, __args.dst, __args.skill_name, __id, __revision)
544 });
545 quote_spanned!(span => Some(__arcdps_gen_export::#func_name as _) )
546 }
547 _ => quote! { None },
548 };
549 (abstract_combat, cb_combat)
550}