gfxd_rs/
macro_info.rs

1/* SPDX-FileCopyrightText: © 2025 Decompollaborate */
2/* SPDX-License-Identifier: MIT OR Apache-2.0 */
3
4use core::{fmt, slice};
5use gfxd_sys::{ffi, macro_info::gfxd_value_t, ptr::NonNullConst};
6
7use crate::{utils, ArgType, MacroId};
8
9const SIZEOF_GFX: usize = 8;
10
11/// An utility to inspect information from the current `Gfx` macro.
12pub struct MacroInfo {
13    // Placeholder to avoid constructing this type
14    _unit: (),
15}
16
17impl MacroInfo {
18    // It should not be possible to construct this type by library consumers.
19    #[must_use]
20    pub(crate) const fn new() -> Self {
21        Self { _unit: () }
22    }
23
24    /// Returns the offset in the input data of the current macro.
25    #[must_use]
26    pub fn macro_offset(&self) -> u32 {
27        let offset = unsafe { gfxd_sys::macro_info::gfxd_macro_offset() };
28        offset as _
29    }
30
31    /// Returns the number of Gfx packets within the current macro.
32    #[must_use]
33    pub fn macro_packets(&self) -> u32 {
34        let offset = unsafe { gfxd_sys::macro_info::gfxd_macro_packets() };
35        offset as _
36    }
37
38    // TODO: I'm not sure how to implement this
39    // foreach_pkt
40
41    /// Returns a slice of the binary data for the current macro.
42    ///
43    /// The data is not byte-swapped.
44    #[must_use]
45    pub fn macro_data(&self) -> &[u8] {
46        let data = unsafe { gfxd_sys::macro_info::gfxd_macro_data() };
47        let data_ptr = data.cast().as_ptr();
48        let byte_len = self.macro_packets() as usize * SIZEOF_GFX;
49
50        // SAFETY: pointer is non-null and the length of the data should be
51        // correct according to gfxd's docs.
52        unsafe { slice::from_raw_parts(data_ptr, byte_len) }
53    }
54
55    /// Returns a number that uniquely identifies the current macro.
56    #[must_use]
57    pub fn macro_id(&self) -> Option<MacroId> {
58        let macro_id_raw = unsafe { gfxd_sys::macro_info::gfxd_macro_id() };
59
60        MacroId::from_u32(macro_id_raw as _)
61    }
62
63    /// Returns the name of the current macro.
64    ///
65    /// If the macro does not have a name (i.e. it's invalid), `None` is
66    /// returned.
67    ///
68    /// If a dynamic display list pointer has been specified, the dynamic `g`
69    /// version is returned. Otherwise the static `gs` version is returned.
70    // This function takes `&mut self` instead of plain `&self` because the
71    // returned data from `gfxd_macro_name` is invalidated on subsequent calls
72    // to said function, so `&mut` is used to ensure a unique pointer only ever
73    // exists.
74    #[must_use]
75    pub fn macro_name(&mut self) -> Option<&str> {
76        let macro_name_raw = unsafe { gfxd_sys::macro_info::gfxd_macro_name() }?;
77
78        // SAFETY: The pointer given by gfxd is a nul-terminated C string, and
79        // the.data should be UTF-8 already.
80        let macro_str = unsafe { utils::str_from_c_str(macro_name_raw) };
81
82        Some(macro_str)
83    }
84
85    /// Returns the number of arguments to the current macro, not including a
86    /// dynamic display list pointer if one has been specified.
87    #[must_use]
88    pub fn arg_count(&self) -> u32 {
89        let count = unsafe { gfxd_sys::macro_info::gfxd_arg_count() };
90
91        count as _
92    }
93
94    /// Returns a number that identifies the type of the argument with index
95    /// `arg_num`, or `None` if `arg_num` is larger than the argument count for
96    /// the current macro
97    #[must_use]
98    pub fn arg_type(&self, arg_num: u32) -> Option<ArgType> {
99        if arg_num >= self.arg_count() {
100            return None;
101        }
102
103        let arg_type_raw = unsafe { gfxd_sys::macro_info::gfxd_arg_type(arg_num as _) };
104
105        ArgType::from_u32(arg_type_raw as _)
106    }
107
108    /// Returns the name of the argument with index `arg_num` or `None` if
109    /// `arg_num` is larger than the argument count for the current macro.
110    ///
111    /// Argument names are not canonical, nor are they needed for macro
112    /// disassembly, but they can be useful for informational and diagnostic
113    /// purposes.
114    #[must_use]
115    pub fn arg_name(&self, arg_num: u32) -> Option<&str> {
116        if arg_num >= self.arg_count() {
117            return None;
118        }
119
120        let buf = unsafe { gfxd_sys::macro_info::gfxd_arg_name(arg_num as _) };
121
122        // SAFETY: The pointer given by gfxd is a nul-terminated C string, and
123        // the.data should be UTF-8 already.
124        let name = unsafe { utils::str_from_c_str(buf) };
125
126        Some(name)
127    }
128
129    /// Returns the value in the current argument.
130    #[must_use]
131    pub fn arg_value(&self, arg_num: u32) -> Option<ArgValue> {
132        if arg_num >= self.arg_count() {
133            return None;
134        }
135
136        let raw_value = unsafe { gfxd_sys::macro_info::gfxd_arg_value(arg_num as _) };
137        let raw_fmt = unsafe { gfxd_sys::macro_info::gfxd_arg_fmt(arg_num as _) };
138
139        ArgValue::new(raw_fmt, raw_value)
140    }
141
142    // TODO
143    // value_by_type
144
145    /// Returns `Some(true)` if the argument with index `arg_num` is "valid",
146    /// for some definition of valid.
147    ///
148    /// An invalid argument generally means that the disassembler found
149    /// inconsistencies in the input data, or that the data can not be
150    /// reproduced by the current macro type.
151    ///
152    /// The argument still has a value that can be printed, though the value is
153    /// not guaranteed to make any sense.
154    #[must_use]
155    pub fn arg_valid(&self, arg_num: u32) -> Option<bool> {
156        if arg_num >= self.arg_count() {
157            return None;
158        }
159
160        let arg_valid = unsafe { gfxd_sys::macro_info::gfxd_arg_valid(arg_num as _) };
161
162        Some(arg_valid != 0)
163    }
164}
165
166/// A value of a `Gfx` argument.
167#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
168#[must_use]
169pub enum ArgValue {
170    /// Signed value
171    I(i32),
172    /// Unsigned value
173    U(u32),
174    /// Float value
175    F(f32),
176}
177
178impl ArgValue {
179    fn new(raw_fmt: ffi::c_int, raw_value: NonNullConst<gfxd_value_t>) -> Option<Self> {
180        let arg_value = unsafe { raw_value.as_ref() };
181
182        match raw_fmt as u32 {
183            gfxd_sys::macro_info::gfxd_argfmt_i => Some(Self::I(unsafe { arg_value.i })),
184            gfxd_sys::macro_info::gfxd_argfmt_u => Some(Self::U(unsafe { arg_value.u })),
185            gfxd_sys::macro_info::gfxd_argfmt_f => Some(Self::F(unsafe { arg_value.f })),
186            _ => None,
187        }
188    }
189
190    pub(crate) const fn to_gfxd_value(
191        self,
192    ) -> (
193        gfxd_sys::macro_info::ArgFmt,
194        gfxd_sys::macro_info::gfxd_value_t,
195    ) {
196        match self {
197            ArgValue::I(x) => (
198                gfxd_sys::macro_info::ArgFmt::gfxd_argfmt_i,
199                gfxd_sys::macro_info::gfxd_value_t { i: x },
200            ),
201            ArgValue::U(x) => (
202                gfxd_sys::macro_info::ArgFmt::gfxd_argfmt_u,
203                gfxd_sys::macro_info::gfxd_value_t { u: x },
204            ),
205            ArgValue::F(x) => (
206                gfxd_sys::macro_info::ArgFmt::gfxd_argfmt_f,
207                gfxd_sys::macro_info::gfxd_value_t { f: x },
208            ),
209        }
210    }
211}
212
213impl fmt::Display for ArgValue {
214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215        match self {
216            ArgValue::I(x) => write!(f, "{}", x),
217            ArgValue::U(x) => write!(f, "0x{:08X}", x),
218            ArgValue::F(x) => write!(f, "{}", x),
219        }
220    }
221}