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}