gfxd_rs/
customizer.rs

1/* SPDX-FileCopyrightText: © 2025 Decompollaborate */
2/* SPDX-License-Identifier: MIT OR Apache-2.0 */
3
4use core::{
5    convert::TryInto as _,
6    num::{NonZeroU16, NonZeroU32},
7    slice,
8};
9use gfxd_sys::{ffi, ptr::NonNullConst};
10
11use crate::{
12    lib_data::LibData, Address, LightsNum, LookatCount, MacroInfo, MacroPrinter, Printer, TexFmt,
13    TexSiz, TlutCount,
14};
15
16/// Customize the output for each `Gfx` macro, and extract data from each macro.
17///
18/// This allows extending or replacing the normal functionality of `gfxd` by
19/// registering callbacks for each specific situation.
20///
21/// Each callback is passed a reference of either [`MacroInfo`], [`Printer`],
22/// and/or [`MacroPrinter`] which allows to inspect the contents of the current
23/// macro or write output to the output buffer.
24///
25/// There are 3 major groups for the registered callbacks:
26/// - [`before_after_execution_callback`]: Callbacks that will be called before
27///   and after the current `gfxd` execution.
28/// - [`macro_fn`]: Replace or extend the behavior for each specific macro.
29/// - Argument callbacks: Callbacks that will be executed when certain types
30///   of macros are encountered.
31///
32/// [`before_after_execution_callback`]: Customizer::before_after_execution_callback
33/// [`macro_fn`]: Customizer::macro_fn
34// 'cls is short for closure
35#[allow(clippy::type_complexity)]
36pub struct Customizer<'cls> {
37    // These two callbacks are called only once during the whole disassembly,so
38    // it would be nice if they were FnOnce, but I couldn't get it to work.
39    before_execution: Option<&'cls mut dyn FnMut(&mut Printer)>,
40    after_execution: Option<&'cls mut dyn FnMut(&mut Printer)>,
41
42    macro_fn: Option<&'cls mut dyn FnMut(&mut MacroPrinter, &mut MacroInfo) -> MacroFnRet>,
43    arg_fn: Option<&'cls mut dyn FnMut(&mut MacroPrinter, &mut MacroInfo, i32)>,
44
45    tlut_fn: Option<
46        &'cls mut dyn FnMut(
47            &mut Printer,
48            &mut MacroInfo,
49            Address,
50            Option<u8>,
51            TlutCount,
52        ) -> DoDefaultOutput,
53    >,
54    timg_fn: Option<
55        &'cls mut dyn FnMut(
56            &mut Printer,
57            &mut MacroInfo,
58            Address,
59            TexFmt,
60            TexSiz,
61            u8,
62            u8,
63            u8,
64        ) -> DoDefaultOutput,
65    >,
66    cimg_fn: Option<
67        &'cls mut dyn FnMut(
68            &mut Printer,
69            &mut MacroInfo,
70            Address,
71            TexFmt,
72            TexSiz,
73            u16,
74        ) -> DoDefaultOutput,
75    >,
76    zimg_fn: Option<&'cls mut dyn FnMut(&mut Printer, &mut MacroInfo, Address) -> DoDefaultOutput>,
77    dl_fn: Option<&'cls mut dyn FnMut(&mut Printer, &mut MacroInfo, Address) -> DoDefaultOutput>,
78    mtx_fn: Option<&'cls mut dyn FnMut(&mut Printer, &mut MacroInfo, Address) -> DoDefaultOutput>,
79    lookat_fn: Option<
80        &'cls mut dyn FnMut(&mut Printer, &mut MacroInfo, Address, LookatCount) -> DoDefaultOutput,
81    >,
82    light_fn: Option<&'cls mut dyn FnMut(&mut Printer, &mut MacroInfo, Address) -> DoDefaultOutput>,
83    lightsn_fn: Option<
84        &'cls mut dyn FnMut(&mut Printer, &mut MacroInfo, Address, LightsNum) -> DoDefaultOutput,
85    >,
86    seg_fn:
87        Option<&'cls mut dyn FnMut(&mut Printer, &mut MacroInfo, Address, u8) -> DoDefaultOutput>,
88    vtx_fn:
89        Option<&'cls mut dyn FnMut(&mut Printer, &mut MacroInfo, Address, i32) -> DoDefaultOutput>,
90    vp_fn: Option<&'cls mut dyn FnMut(&mut Printer, &mut MacroInfo, Address) -> DoDefaultOutput>,
91    uctext_fn: Option<
92        &'cls mut dyn FnMut(&mut Printer, &mut MacroInfo, Address, NonZeroU32) -> DoDefaultOutput,
93    >,
94    ucdata_fn: Option<
95        &'cls mut dyn FnMut(&mut Printer, &mut MacroInfo, Address, NonZeroU32) -> DoDefaultOutput,
96    >,
97    dram_fn: Option<
98        &'cls mut dyn FnMut(&mut Printer, &mut MacroInfo, Address, NonZeroU16) -> DoDefaultOutput,
99    >,
100}
101
102impl Customizer<'_> {
103    pub(crate) fn apply_callbacks(&mut self) {
104        self.apply_out_callback();
105        self.apply_user_macro_callbacks();
106        self.apply_user_arg_callbacks();
107    }
108
109    fn apply_out_callback(&mut self) {
110        // Write to an out buffer
111        extern "C" fn output_callback(
112            buf: NonNullConst<ffi::c_char>,
113            count: ffi::c_int,
114        ) -> ffi::c_int {
115            // Retrieve the out_buf from the user data pointer.
116            let lib_data = LibData::get().expect("Welp. Maybe race condition?");
117
118            // SAFETY: We just have to trust the length of the data is correct.
119            let raw_slice = unsafe { slice::from_raw_parts(buf.as_ptr().cast(), count as _) };
120
121            // SAFETY: gfxd doesn't use non-ascii characters, and everything
122            // else should be generated by the user, which should be UTF-8
123            // already, since that's the only thing the API supports.
124            let data_str = unsafe { core::str::from_utf8_unchecked(raw_slice) };
125
126            // Push the output into our buffer.
127            lib_data.write_to_buf(data_str);
128
129            // We read the whole buffer.
130            count
131        }
132
133        // Write the disassembly output to out_buf.
134        // We pass it around by passing it as a user data pointer (LibData).
135        unsafe {
136            gfxd_sys::io::gfxd_output_callback(Some(output_callback));
137        }
138    }
139
140    fn apply_user_macro_callbacks(&mut self) {
141        if self.macro_fn.is_some() {
142            extern "C" fn callback() -> ffi::c_int {
143                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
144
145                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().macro_fn {
146                    let mut printer = MacroPrinter::new();
147                    let mut info = MacroInfo::new();
148                    (closure)(&mut printer, &mut info)
149                } else {
150                    panic!("macro_fn closure was None?")
151                };
152
153                ret.into_ret()
154            }
155            unsafe {
156                gfxd_sys::handlers::gfxd_macro_fn(Some(callback));
157            }
158        } else {
159            unsafe {
160                gfxd_sys::handlers::gfxd_macro_fn(None);
161            }
162        }
163
164        if self.arg_fn.is_some() {
165            extern "C" fn callback(arg_num: ffi::c_int) {
166                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
167
168                if let Some(closure) = &mut lib_data.get_customizer_mut().arg_fn {
169                    let mut printer = MacroPrinter::new();
170                    let mut info = MacroInfo::new();
171                    (closure)(&mut printer, &mut info, arg_num);
172                } else {
173                    panic!("arg_fn closure was None?")
174                }
175            }
176            unsafe {
177                gfxd_sys::handlers::gfxd_arg_fn(Some(callback));
178            }
179        } else {
180            unsafe {
181                gfxd_sys::handlers::gfxd_arg_fn(None);
182            }
183        }
184    }
185
186    fn apply_user_arg_callbacks(&mut self) {
187        if self.tlut_fn.is_some() {
188            extern "C" fn callback(tlut: u32, idx: i32, count: i32) -> ffi::c_int {
189                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
190
191                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().tlut_fn {
192                    let mut printer = Printer::new();
193                    let mut info = MacroInfo::new();
194                    let timg = Address(tlut);
195                    #[allow(clippy::cast_possible_truncation)]
196                    let index = if idx == -1 { None } else { Some(idx as u8) };
197                    let tlut_count = TlutCount::new(count);
198                    (closure)(&mut printer, &mut info, timg, index, tlut_count)
199                } else {
200                    panic!("tlut_fn closure was None?")
201                };
202
203                ret.into_ret()
204            }
205            unsafe {
206                gfxd_sys::argument_callbacks::gfxd_tlut_callback(Some(callback));
207            }
208        } else {
209            unsafe {
210                gfxd_sys::argument_callbacks::gfxd_tlut_callback(None);
211            }
212        }
213
214        if self.timg_fn.is_some() {
215            extern "C" fn callback(
216                timg: u32,
217                fmt: i32,
218                siz: i32,
219                width: i32,
220                height: i32,
221                pal: i32,
222            ) -> ffi::c_int {
223                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
224
225                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().timg_fn {
226                    let mut printer = Printer::new();
227                    let mut info = MacroInfo::new();
228                    let timg = Address(timg);
229                    let tex_fmt = TexFmt::new(fmt);
230                    let tex_siz = TexSiz::new(siz);
231                    #[allow(clippy::cast_possible_truncation)]
232                    let width = width as _;
233                    #[allow(clippy::cast_possible_truncation)]
234                    let height = height as _;
235                    #[allow(clippy::cast_possible_truncation)]
236                    let pal = pal as _;
237                    (closure)(
238                        &mut printer,
239                        &mut info,
240                        timg,
241                        tex_fmt,
242                        tex_siz,
243                        width,
244                        height,
245                        pal,
246                    )
247                } else {
248                    panic!("timg_fn closure was None?")
249                };
250
251                ret.into_ret()
252            }
253            unsafe {
254                gfxd_sys::argument_callbacks::gfxd_timg_callback(Some(callback));
255            }
256        } else {
257            unsafe {
258                gfxd_sys::argument_callbacks::gfxd_timg_callback(None);
259            }
260        }
261
262        if self.cimg_fn.is_some() {
263            extern "C" fn callback(cimg: u32, fmt: i32, siz: i32, width: i32) -> ffi::c_int {
264                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
265
266                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().cimg_fn {
267                    let mut printer = Printer::new();
268                    let mut info = MacroInfo::new();
269                    let cimg = Address(cimg);
270                    let tex_fmt = TexFmt::new(fmt);
271                    let tex_siz = TexSiz::new(siz);
272                    #[allow(clippy::cast_possible_truncation)]
273                    let width = width as _;
274                    (closure)(&mut printer, &mut info, cimg, tex_fmt, tex_siz, width)
275                } else {
276                    panic!("cimg_fn closure was None?")
277                };
278
279                ret.into_ret()
280            }
281            unsafe {
282                gfxd_sys::argument_callbacks::gfxd_cimg_callback(Some(callback));
283            }
284        } else {
285            unsafe {
286                gfxd_sys::argument_callbacks::gfxd_cimg_callback(None);
287            }
288        }
289
290        if self.zimg_fn.is_some() {
291            extern "C" fn callback(zimg: u32) -> ffi::c_int {
292                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
293
294                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().zimg_fn {
295                    let mut printer = Printer::new();
296                    let mut info = MacroInfo::new();
297                    let zimg = Address(zimg);
298                    (closure)(&mut printer, &mut info, zimg)
299                } else {
300                    panic!("zimg_fn closure was None?")
301                };
302
303                ret.into_ret()
304            }
305            unsafe {
306                gfxd_sys::argument_callbacks::gfxd_zimg_callback(Some(callback));
307            }
308        } else {
309            unsafe {
310                gfxd_sys::argument_callbacks::gfxd_zimg_callback(None);
311            }
312        }
313
314        if self.dl_fn.is_some() {
315            extern "C" fn callback(dl: u32) -> ffi::c_int {
316                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
317
318                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().dl_fn {
319                    let mut printer = Printer::new();
320                    let mut info = MacroInfo::new();
321                    let dl = Address(dl);
322                    (closure)(&mut printer, &mut info, dl)
323                } else {
324                    panic!("dl_fn closure was None?")
325                };
326
327                ret.into_ret()
328            }
329            unsafe {
330                gfxd_sys::argument_callbacks::gfxd_dl_callback(Some(callback));
331            }
332        } else {
333            unsafe {
334                gfxd_sys::argument_callbacks::gfxd_dl_callback(None);
335            }
336        }
337
338        if self.mtx_fn.is_some() {
339            extern "C" fn callback(mtx: u32) -> ffi::c_int {
340                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
341
342                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().mtx_fn {
343                    let mut printer = Printer::new();
344                    let mut info = MacroInfo::new();
345                    let mtx = Address(mtx);
346                    (closure)(&mut printer, &mut info, mtx)
347                } else {
348                    panic!("mtx_fn closure was None?")
349                };
350
351                ret.into_ret()
352            }
353            unsafe {
354                gfxd_sys::argument_callbacks::gfxd_mtx_callback(Some(callback));
355            }
356        } else {
357            unsafe {
358                gfxd_sys::argument_callbacks::gfxd_mtx_callback(None);
359            }
360        }
361
362        if self.lookat_fn.is_some() {
363            extern "C" fn callback(lookat: u32, count: i32) -> ffi::c_int {
364                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
365
366                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().lookat_fn {
367                    let mut printer = Printer::new();
368                    let mut info = MacroInfo::new();
369                    let lookat = Address(lookat);
370                    let count = LookatCount::new(count);
371                    (closure)(&mut printer, &mut info, lookat, count)
372                } else {
373                    panic!("lookat_fn closure was None?")
374                };
375
376                ret.into_ret()
377            }
378            unsafe {
379                gfxd_sys::argument_callbacks::gfxd_lookat_callback(Some(callback));
380            }
381        } else {
382            unsafe {
383                gfxd_sys::argument_callbacks::gfxd_lookat_callback(None);
384            }
385        }
386
387        if self.light_fn.is_some() {
388            extern "C" fn callback(light: u32) -> ffi::c_int {
389                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
390
391                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().light_fn {
392                    let mut printer = Printer::new();
393                    let mut info = MacroInfo::new();
394                    let light = Address(light);
395                    (closure)(&mut printer, &mut info, light)
396                } else {
397                    panic!("light_fn closure was None?")
398                };
399
400                ret.into_ret()
401            }
402            unsafe {
403                gfxd_sys::argument_callbacks::gfxd_light_callback(Some(callback));
404            }
405        } else {
406            unsafe {
407                gfxd_sys::argument_callbacks::gfxd_light_callback(None);
408            }
409        }
410
411        if self.lightsn_fn.is_some() {
412            extern "C" fn callback(lightsn: u32, num: i32) -> ffi::c_int {
413                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
414
415                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().lightsn_fn {
416                    let mut printer = Printer::new();
417                    let mut info = MacroInfo::new();
418                    let lightsn = Address(lightsn);
419                    let num = LightsNum::new(num);
420                    (closure)(&mut printer, &mut info, lightsn, num)
421                } else {
422                    panic!("lightsn_fn closure was None?")
423                };
424
425                ret.into_ret()
426            }
427            unsafe {
428                gfxd_sys::argument_callbacks::gfxd_lightsn_callback(Some(callback));
429            }
430        } else {
431            unsafe {
432                gfxd_sys::argument_callbacks::gfxd_lightsn_callback(None);
433            }
434        }
435
436        if self.seg_fn.is_some() {
437            extern "C" fn callback(seg: u32, num: i32) -> ffi::c_int {
438                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
439
440                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().seg_fn {
441                    let mut printer = Printer::new();
442                    let mut info = MacroInfo::new();
443                    let seg = Address(seg);
444                    #[allow(clippy::cast_possible_truncation)]
445                    let num = num as _;
446                    (closure)(&mut printer, &mut info, seg, num)
447                } else {
448                    panic!("seg_fn closure was None?")
449                };
450
451                ret.into_ret()
452            }
453            unsafe {
454                gfxd_sys::argument_callbacks::gfxd_seg_callback(Some(callback));
455            }
456        } else {
457            unsafe {
458                gfxd_sys::argument_callbacks::gfxd_seg_callback(None);
459            }
460        }
461
462        if self.vtx_fn.is_some() {
463            extern "C" fn callback(vtx: u32, num: i32) -> ffi::c_int {
464                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
465
466                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().vtx_fn {
467                    let mut printer = Printer::new();
468                    let mut info = MacroInfo::new();
469                    let vtx = Address(vtx);
470                    (closure)(&mut printer, &mut info, vtx, num)
471                } else {
472                    panic!("vtx_fn closure was None?")
473                };
474
475                ret.into_ret()
476            }
477            unsafe {
478                gfxd_sys::argument_callbacks::gfxd_vtx_callback(Some(callback));
479            }
480        } else {
481            unsafe {
482                gfxd_sys::argument_callbacks::gfxd_vtx_callback(None);
483            }
484        }
485
486        if self.vp_fn.is_some() {
487            extern "C" fn callback(vp: u32) -> ffi::c_int {
488                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
489
490                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().vp_fn {
491                    let mut printer = Printer::new();
492                    let mut info = MacroInfo::new();
493                    let vp = Address(vp);
494                    (closure)(&mut printer, &mut info, vp)
495                } else {
496                    panic!("vp_fn closure was None?")
497                };
498
499                ret.into_ret()
500            }
501            unsafe {
502                gfxd_sys::argument_callbacks::gfxd_vp_callback(Some(callback));
503            }
504        } else {
505            unsafe {
506                gfxd_sys::argument_callbacks::gfxd_vp_callback(None);
507            }
508        }
509
510        if self.uctext_fn.is_some() {
511            extern "C" fn callback(uctext: u32, size: NonZeroU32) -> ffi::c_int {
512                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
513
514                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().uctext_fn {
515                    let mut printer = Printer::new();
516                    let mut info = MacroInfo::new();
517                    let uctext = Address(uctext);
518                    (closure)(&mut printer, &mut info, uctext, size)
519                } else {
520                    panic!("uctext_fn closure was None?")
521                };
522
523                ret.into_ret()
524            }
525            unsafe {
526                gfxd_sys::argument_callbacks::gfxd_uctext_callback(Some(callback));
527            }
528        } else {
529            unsafe {
530                gfxd_sys::argument_callbacks::gfxd_uctext_callback(None);
531            }
532        }
533
534        if self.ucdata_fn.is_some() {
535            extern "C" fn callback(ucdata: u32, size: NonZeroU32) -> ffi::c_int {
536                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
537
538                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().ucdata_fn {
539                    let mut printer = Printer::new();
540                    let mut info = MacroInfo::new();
541                    let ucdata = Address(ucdata);
542                    (closure)(&mut printer, &mut info, ucdata, size)
543                } else {
544                    panic!("ucdata_fn closure was None?")
545                };
546
547                ret.into_ret()
548            }
549            unsafe {
550                gfxd_sys::argument_callbacks::gfxd_ucdata_callback(Some(callback));
551            }
552        } else {
553            unsafe {
554                gfxd_sys::argument_callbacks::gfxd_ucdata_callback(None);
555            }
556        }
557
558        if self.dram_fn.is_some() {
559            extern "C" fn callback(dram: u32, size: NonZeroU32) -> ffi::c_int {
560                let lib_data = LibData::get().expect("Welp. Maybe race condition?");
561
562                let ret = if let Some(closure) = &mut lib_data.get_customizer_mut().dram_fn {
563                    let mut printer = Printer::new();
564                    let mut info = MacroInfo::new();
565                    let dram = Address(dram);
566                    let size_raw = size
567                        .get()
568                        .try_into()
569                        .expect("This value should be less than 0x4096");
570                    #[allow(clippy::shadow_unrelated)]
571                    let size = unsafe { NonZeroU16::new_unchecked(size_raw) };
572                    (closure)(&mut printer, &mut info, dram, size)
573                } else {
574                    panic!("dram_fn closure was None?")
575                };
576
577                ret.into_ret()
578            }
579            unsafe {
580                gfxd_sys::argument_callbacks::gfxd_dram_callback(Some(callback));
581            }
582        } else {
583            unsafe {
584                gfxd_sys::argument_callbacks::gfxd_dram_callback(None);
585            }
586        }
587    }
588}
589
590impl Customizer<'_> {
591    /// Create a new instance of the customizer.
592    #[must_use]
593    pub fn new() -> Self {
594        Self {
595            before_execution: None,
596            after_execution: None,
597            macro_fn: None,
598            arg_fn: None,
599            tlut_fn: None,
600            timg_fn: None,
601            cimg_fn: None,
602            zimg_fn: None,
603            dl_fn: None,
604            mtx_fn: None,
605            lookat_fn: None,
606            light_fn: None,
607            lightsn_fn: None,
608            seg_fn: None,
609            vtx_fn: None,
610            vp_fn: None,
611            uctext_fn: None,
612            ucdata_fn: None,
613            dram_fn: None,
614        }
615    }
616}
617
618impl<'cls> Customizer<'cls> {
619    /// Register callbacks that are called before and after the execution of
620    /// `gfxd`.
621    ///
622    /// Allows to do some setup work for the output buffer, like writing
623    /// opening and closing braces.
624    ///
625    /// `before` is called before `gfxd` starts executing, so the output buffer
626    /// will be empty at this point. `after` is called once `gfxd` finishes
627    /// running, and it is the very last code that is run before returning the
628    /// disassembled output to the user.
629    ///
630    /// Both callbacks are called only once.
631    ///
632    /// ## Examples
633    ///
634    /// ```rust
635    /// use gfxd_rs::{Customizer, Printer};
636    ///
637    /// let mut before = |printer: &mut Printer| {
638    ///     printer.write_str("{\n");
639    /// };
640    /// let mut after = |printer: &mut Printer| {
641    ///     printer.write_str("}\n");
642    /// };
643    ///
644    /// let mut customizer = Customizer::new();
645    /// customizer.before_after_execution_callback(&mut before, &mut after);
646    /// ```
647    pub fn before_after_execution_callback<B, A>(
648        &mut self,
649        before: &'cls mut B,
650        after: &'cls mut A,
651    ) -> &mut Self
652    where
653        B: FnMut(&mut Printer),
654        A: FnMut(&mut Printer),
655    {
656        self.before_execution = Some(before);
657        self.after_execution = Some(after);
658
659        self
660    }
661
662    pub(crate) fn do_before(&mut self) {
663        if let Some(closure) = &mut self.before_execution {
664            let mut printer = Printer::new();
665            (closure)(&mut printer);
666        }
667    }
668    pub(crate) fn do_after(&mut self) {
669        if let Some(closure) = &mut self.after_execution {
670            let mut printer = Printer::new();
671            (closure)(&mut printer);
672        }
673    }
674}
675
676impl<'cls> Customizer<'cls> {
677    /// Replace the default macro handler.
678    ///
679    /// The user-defined macro handler may decide to extend the default
680    /// behavior by writing custom output with [`write_str`] and calling the
681    /// original macro handler by calling [`macro_dflt`].
682    ///
683    /// ## Example
684    ///
685    /// Make the output pretier
686    ///
687    /// ```rust
688    /// use gfxd_rs::{Customizer, MacroPrinter};
689    ///
690    /// let mut macro_fn = |printer: &mut MacroPrinter, _info: &mut _| {
691    ///     /* Print 4 spaces before each macro, and a comma and newline after each macro */
692    ///     printer.write_str("    ");
693    ///     let ret = printer.macro_dflt(); /* Execute the default macro handler */
694    ///     printer.write_str(",\n");
695    ///     ret
696    /// };
697    ///
698    /// let mut customizer = Customizer::new();
699    /// customizer.macro_fn(&mut macro_fn);
700    /// ```
701    ///
702    /// [`write_str`]: MacroPrinter::write_str
703    /// [`macro_dflt`]: MacroPrinter::macro_dflt
704    pub fn macro_fn<F>(&mut self, callback: &'cls mut F) -> &mut Self
705    where
706        F: FnMut(&mut MacroPrinter, &mut MacroInfo) -> MacroFnRet,
707    {
708        self.macro_fn = Some(callback);
709
710        self
711    }
712
713    /// Replace the default argument handler.
714    ///
715    /// The registered callback will be called by [`macro_dflt`] for each
716    /// argument in the current macro, not counting the dynamic display list
717    /// pointer if one has been specified.
718    ///
719    /// If the user has overriden the macro handler function and does not call
720    /// [`macro_dflt`] in the replacement, then the given callback will never
721    /// be called.
722    ///
723    /// [`macro_dflt`]: MacroPrinter::macro_dflt
724    pub fn arg_fn<F>(&mut self, callback: &'cls mut F) -> &mut Self
725    where
726        F: FnMut(&mut MacroPrinter, &mut MacroInfo, i32),
727    {
728        self.arg_fn = Some(callback);
729
730        self
731    }
732}
733
734impl<'cls> Customizer<'cls> {
735    /// Register a callback for TLUTs (Texture Look Up Table), a.k.a. palettes.
736    ///
737    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
738    /// to emit the default output for this argument, or
739    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
740    /// just use whatever the user printed.
741    ///
742    /// Emit any desired output by using the `Printer` argument, and inspect
743    /// information about the current macro with the `MacroInfo` argument.
744    ///
745    /// The other arguments are:
746    /// - the tlut address,
747    /// - the palette index and
748    /// - the number of colors.
749    pub fn tlut_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
750    where
751        F: FnMut(&mut Printer, &mut MacroInfo, Address, Option<u8>, TlutCount) -> DoDefaultOutput,
752    {
753        self.tlut_fn = Some(callback);
754        self
755    }
756
757    /// Register a callback for textures of any kind.
758    ///
759    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
760    /// to emit the default output for this argument, or
761    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
762    /// just use whatever the user printed.
763    ///
764    /// Emit any desired output by using the `Printer` argument, and inspect
765    /// information about the current macro with the `MacroInfo` argument.
766    ///
767    /// The other arguments are:
768    /// - the texture address,
769    /// - the texture format,
770    /// - the texture siz,
771    /// - the width,
772    /// - the height and
773    /// - the palette index.
774    pub fn timg_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
775    where
776        F: FnMut(
777            &mut Printer,
778            &mut MacroInfo,
779            Address,
780            TexFmt,
781            TexSiz,
782            u8,
783            u8,
784            u8,
785        ) -> DoDefaultOutput,
786    {
787        self.timg_fn = Some(callback);
788        self
789    }
790
791    /// Register a callback for framebuffers.
792    ///
793    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
794    /// to emit the default output for this argument, or
795    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
796    /// just use whatever the user printed.
797    ///
798    /// Emit any desired output by using the `Printer` argument, and inspect
799    /// information about the current macro with the `MacroInfo` argument.
800    ///
801    /// The other arguments are:
802    /// - the framebuffer address,
803    /// - the framebuffer format,
804    /// - the framebuffer siz and
805    /// - the width.
806    pub fn cimg_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
807    where
808        F: FnMut(&mut Printer, &mut MacroInfo, Address, TexFmt, TexSiz, u16) -> DoDefaultOutput,
809    {
810        self.cimg_fn = Some(callback);
811        self
812    }
813
814    /// Register a callback for depthbuffers.
815    ///
816    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
817    /// to emit the default output for this argument, or
818    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
819    /// just use whatever the user printed.
820    ///
821    /// Emit any desired output by using the `Printer` argument, and inspect
822    /// information about the current macro with the `MacroInfo` argument.
823    ///
824    /// The other arguments are:
825    /// - the depthbuffer address.
826    pub fn zimg_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
827    where
828        F: FnMut(&mut Printer, &mut MacroInfo, Address) -> DoDefaultOutput,
829    {
830        self.zimg_fn = Some(callback);
831        self
832    }
833
834    /// Register a callback for display lists.
835    ///
836    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
837    /// to emit the default output for this argument, or
838    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
839    /// just use whatever the user printed.
840    ///
841    /// Emit any desired output by using the `Printer` argument, and inspect
842    /// information about the current macro with the `MacroInfo` argument.
843    ///
844    /// The other arguments are:
845    /// - the display list address.
846    pub fn dl_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
847    where
848        F: FnMut(&mut Printer, &mut MacroInfo, Address) -> DoDefaultOutput,
849    {
850        self.dl_fn = Some(callback);
851        self
852    }
853
854    /// Register a callback for matrices.
855    ///
856    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
857    /// to emit the default output for this argument, or
858    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
859    /// just use whatever the user printed.
860    ///
861    /// Emit any desired output by using the `Printer` argument, and inspect
862    /// information about the current macro with the `MacroInfo` argument.
863    ///
864    /// The other arguments are:
865    /// - the matrix address.
866    pub fn mtx_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
867    where
868        F: FnMut(&mut Printer, &mut MacroInfo, Address) -> DoDefaultOutput,
869    {
870        self.mtx_fn = Some(callback);
871        self
872    }
873
874    /// Register a callback for lookat arrays.
875    ///
876    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
877    /// to emit the default output for this argument, or
878    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
879    /// just use whatever the user printed.
880    ///
881    /// Emit any desired output by using the `Printer` argument, and inspect
882    /// information about the current macro with the `MacroInfo` argument.
883    ///
884    /// The other arguments are:
885    /// - the lookat array address and
886    /// - the number of lookat structures.
887    pub fn lookat_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
888    where
889        F: FnMut(&mut Printer, &mut MacroInfo, Address, LookatCount) -> DoDefaultOutput,
890    {
891        self.lookat_fn = Some(callback);
892        self
893    }
894
895    /// Register a callback for lights.
896    ///
897    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
898    /// to emit the default output for this argument, or
899    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
900    /// just use whatever the user printed.
901    ///
902    /// Emit any desired output by using the `Printer` argument, and inspect
903    /// information about the current macro with the `MacroInfo` argument.
904    ///
905    /// The other arguments are:
906    /// - the light address.
907    pub fn light_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
908    where
909        F: FnMut(&mut Printer, &mut MacroInfo, Address) -> DoDefaultOutput,
910    {
911        self.light_fn = Some(callback);
912        self
913    }
914
915    /// Register a callback for lights N.
916    ///
917    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
918    /// to emit the default output for this argument, or
919    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
920    /// just use whatever the user printed.
921    ///
922    /// Emit any desired output by using the `Printer` argument, and inspect
923    /// information about the current macro with the `MacroInfo` argument.
924    ///
925    /// The other arguments are:
926    /// - the light address and
927    /// - the number of diffuse lights.
928    pub fn lightsn_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
929    where
930        F: FnMut(&mut Printer, &mut MacroInfo, Address, LightsNum) -> DoDefaultOutput,
931    {
932        self.lightsn_fn = Some(callback);
933        self
934    }
935
936    /// Register a callback for segments.
937    ///
938    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
939    /// to emit the default output for this argument, or
940    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
941    /// just use whatever the user printed.
942    ///
943    /// Emit any desired output by using the `Printer` argument, and inspect
944    /// information about the current macro with the `MacroInfo` argument.
945    ///
946    /// The other arguments are:
947    /// - the segment address.
948    pub fn seg_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
949    where
950        F: FnMut(&mut Printer, &mut MacroInfo, Address, u8) -> DoDefaultOutput,
951    {
952        self.seg_fn = Some(callback);
953        self
954    }
955
956    /// Register a callback for vertices.
957    ///
958    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
959    /// to emit the default output for this argument, or
960    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
961    /// just use whatever the user printed.
962    ///
963    /// Emit any desired output by using the `Printer` argument, and inspect
964    /// information about the current macro with the `MacroInfo` argument.
965    ///
966    /// The other arguments are:
967    /// - the vertex address.
968    pub fn vtx_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
969    where
970        F: FnMut(&mut Printer, &mut MacroInfo, Address, i32) -> DoDefaultOutput,
971    {
972        self.vtx_fn = Some(callback);
973        self
974    }
975
976    /// Register a callback for viewports.
977    ///
978    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
979    /// to emit the default output for this argument, or
980    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
981    /// just use whatever the user printed.
982    ///
983    /// Emit any desired output by using the `Printer` argument, and inspect
984    /// information about the current macro with the `MacroInfo` argument.
985    ///
986    /// The other arguments are:
987    /// - the viewport address.
988    pub fn vp_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
989    where
990        F: FnMut(&mut Printer, &mut MacroInfo, Address) -> DoDefaultOutput,
991    {
992        self.vp_fn = Some(callback);
993        self
994    }
995
996    /// Register a callback for microcode text.
997    ///
998    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
999    /// to emit the default output for this argument, or
1000    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
1001    /// just use whatever the user printed.
1002    ///
1003    /// Emit any desired output by using the `Printer` argument, and inspect
1004    /// information about the current macro with the `MacroInfo` argument.
1005    ///
1006    /// The other arguments are:
1007    /// - the microcode text address and
1008    /// - the microcode text size.
1009    pub fn uctext_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
1010    where
1011        F: FnMut(&mut Printer, &mut MacroInfo, Address, NonZeroU32) -> DoDefaultOutput,
1012    {
1013        self.uctext_fn = Some(callback);
1014        self
1015    }
1016
1017    /// Register a callback for microcode data.
1018    ///
1019    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
1020    /// to emit the default output for this argument, or
1021    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
1022    /// just use whatever the user printed.
1023    ///
1024    /// Emit any desired output by using the `Printer` argument, and inspect
1025    /// information about the current macro with the `MacroInfo` argument.
1026    ///
1027    /// The other arguments are:
1028    /// - the microcode data address and
1029    /// - the microcode data size.
1030    pub fn ucdata_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
1031    where
1032        F: FnMut(&mut Printer, &mut MacroInfo, Address, NonZeroU32) -> DoDefaultOutput,
1033    {
1034        self.ucdata_fn = Some(callback);
1035        self
1036    }
1037
1038    /// Register a callback for generic pointers.
1039    ///
1040    /// The callback may return [`DoDefaultOutput::DoDefault`] to make `gfxd`
1041    /// to emit the default output for this argument, or
1042    /// [`DoDefaultOutput::Override`] to avoid emitting the default one and
1043    /// just use whatever the user printed.
1044    ///
1045    /// Emit any desired output by using the `Printer` argument, and inspect
1046    /// information about the current macro with the `MacroInfo` argument.
1047    ///
1048    /// The other arguments are:
1049    /// - the generic pointer and
1050    /// - the data size.
1051    pub fn dram_callback<F>(&mut self, callback: &'cls mut F) -> &mut Self
1052    where
1053        F: FnMut(&mut Printer, &mut MacroInfo, Address, NonZeroU16) -> DoDefaultOutput,
1054    {
1055        self.dram_fn = Some(callback);
1056        self
1057    }
1058}
1059
1060impl Default for Customizer<'_> {
1061    fn default() -> Self {
1062        Self::new()
1063    }
1064}
1065
1066/// The return value of argument callbacks.
1067///
1068/// Decide if the argument callback should emit the default output or if it
1069/// should emit no output at all.
1070#[must_use]
1071pub enum DoDefaultOutput {
1072    /// Emit the default output.
1073    ///
1074    /// The user may still emit output before the default behavior.
1075    DoDefault,
1076    /// Do not emit the default output for this argument.
1077    Override,
1078}
1079
1080impl DoDefaultOutput {
1081    #[inline]
1082    #[must_use]
1083    pub(crate) const fn into_ret(self) -> ffi::c_int {
1084        match self {
1085            Self::DoDefault => 0,
1086            Self::Override => 1,
1087        }
1088    }
1089}
1090
1091/// Signal if `Gfx` packet processing should continue or stop after the current
1092/// macro has been processed.
1093#[must_use]
1094pub enum MacroFnRet {
1095    /// Continue processing the data stream.
1096    Continue,
1097    /// Stop processing the data stream, even if there's more data left to be
1098    /// processed.
1099    Stop,
1100}
1101
1102impl MacroFnRet {
1103    #[inline]
1104    #[must_use]
1105    pub(crate) const fn into_ret(self) -> ffi::c_int {
1106        match self {
1107            Self::Continue => 0,
1108            Self::Stop => 1,
1109        }
1110    }
1111}