arcdps_codegen/
export.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use crate::{
    abi::{C_ABI, SYSTEM_ABI},
    ArcDpsGen,
};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use std::env;
use syn::LitStr;

impl ArcDpsGen {
    /// Generates a literal with the plugin's name.
    pub fn gen_name(&self) -> LitStr {
        let (raw_name, span) = if let Some(input_name) = &self.name {
            let name = input_name.value();
            (name, input_name.span())
        } else {
            let name = env::var("CARGO_PKG_NAME").expect("CARGO_PKG_NAME is not set");
            (name, Span::call_site())
        };
        LitStr::new(raw_name.as_str(), span)
    }

    /// Generates a null-terminated literal with the plugin's name.
    pub fn gen_name_cstr(&self) -> LitStr {
        let name = self.gen_name();

        LitStr::new((name.value() + "\0").as_str(), name.span())
    }

    /// Generates the build/version of the plugin as null-terminated literal.
    pub fn gen_build(&self) -> LitStr {
        let build = env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION is not set") + "\0";
        LitStr::new(build.as_str(), Span::call_site())
    }

    /// Generates the plugin's exports.
    pub fn build_export(&self) -> TokenStream {
        let name = self.gen_name();
        let name_c = self.gen_name_cstr();
        let build = self.gen_build();
        let sig = &self.sig;

        let (init_func, init) = self.build_init().into_tuple();
        let (release_func, release) = self.build_release().into_tuple();
        let update_url = self.build_update_url();

        let (combat_func, combat_value) = self.build_combat().into_tuple();
        let (combat_local_func, combat_local_value) = self.build_combat_local().into_tuple();
        let (imgui_func, imgui_value) = self.build_imgui().into_tuple();
        let (options_end_func, options_end_value) = self.build_options_end().into_tuple();
        let (options_windows_func, options_windows_value) =
            self.build_options_windows().into_tuple();
        let (wnd_filter_func, wnd_filter_value) = self.build_wnd_filter().into_tuple();
        let (wnd_nofilter_func, wnd_nofilter_value) = self.build_wnd_nofilter().into_tuple();

        // TODO: instantiate unused supported fields for intellisense & type checking?
        // TODO: const in own module without access to other mods

        quote! {
            /// ArcDPS export struct with plugin information.
            static __EXPORT: ::arcdps::callbacks::ArcDpsExport = ::arcdps::callbacks::ArcDpsExport {
                size: ::std::mem::size_of::<::arcdps::callbacks::ArcDpsExport>(),
                sig: #sig,
                imgui_version: 18000,
                out_build: #build.as_ptr() as _,
                out_name: #name_c.as_ptr() as _,
                combat: #combat_value,
                combat_local: #combat_local_value,
                imgui: #imgui_value,
                options_end: #options_end_value,
                options_windows: #options_windows_value,
                wnd_filter: #wnd_filter_value,
                wnd_nofilter: #wnd_nofilter_value,
            };

            #init_func

            extern #C_ABI fn __load() -> *const ::arcdps::callbacks::ArcDpsExport {
                /// ArcDPS export struct with error information.
                static mut EXPORT_ERROR: ::arcdps::callbacks::ArcDpsExport = ::arcdps::callbacks::ArcDpsExport {
                    size: 0,
                    sig: 0,
                    imgui_version: 18000,
                    out_build: #build.as_ptr() as _,
                    out_name: #name_c.as_ptr() as _,
                    combat: ::std::option::Option::None,
                    combat_local: ::std::option::Option::None,
                    imgui: ::std::option::Option::None,
                    options_end: ::std::option::Option::None,
                    options_windows: ::std::option::Option::None,
                    wnd_filter: ::std::option::Option::None,
                    wnd_nofilter: ::std::option::Option::None,
                };
                static mut ERROR_STRING: ::std::string::String = ::std::string::String::new();

                let result: ::std::result::Result<(), ::std::string::String> = #init;
                if let ::std::result::Result::Err(err) = result {
                    unsafe {
                        ERROR_STRING = err + "\0";
                        EXPORT_ERROR.size = ERROR_STRING.as_ptr() as _;
                        &EXPORT_ERROR
                    }
                } else {
                    &self::__EXPORT
                }
            }

            #release_func

            extern #C_ABI fn __unload() {
                #release
            }

            /// ArcDPS looks for this exported function and calls the address it returns on client load.
            /// If you need any of the ignored values, create an issue with your use case.
            #[no_mangle]
            pub unsafe extern #SYSTEM_ABI fn get_init_addr(
                arc_version: *const ::arcdps::__macro::c_char,
                imgui_ctx: *mut ::arcdps::imgui::sys::ImGuiContext,
                id3d: *mut ::arcdps::__macro::c_void,
                arc_dll: ::arcdps::__macro::HMODULE,
                malloc: ::std::option::Option<::arcdps::__macro::MallocFn>,
                free: ::std::option::Option<::arcdps::__macro::FreeFn>,
                d3d_version: ::std::primitive::u32,
            ) -> *mut ::arcdps::__macro::c_void {
                ::arcdps::__macro::init(arc_version, arc_dll, imgui_ctx, malloc, free, id3d, d3d_version, #name);
                self::__load as _
            }

            /// ArcDPS looks for this exported function and calls the address it returns on client exit.
            #[no_mangle]
            pub extern #SYSTEM_ABI fn get_release_addr() -> *mut ::arcdps::__macro::c_void {
                self::__unload as _
            }

            #update_url

            #combat_func
            #combat_local_func
            #imgui_func
            #options_end_func
            #options_windows_func
            #wnd_filter_func
            #wnd_nofilter_func
        }
    }
}