1use binaryninjacore_sys::*;
4use core::ffi;
5use ffi::c_void;
6use std::fmt::Debug;
7use std::ptr::NonNull;
8
9use crate::binary_view::BinaryView;
10use crate::disassembly::{DisassemblyTextLine, InstructionTextToken};
11use crate::rc::Array;
12use crate::string::BnString;
13use crate::types::Type;
14
15pub fn register_data_renderer<C: CustomDataRenderer>(
17 custom: C,
18) -> (&'static mut C, CoreDataRenderer) {
19 let renderer = Box::leak(Box::new(custom));
20 let mut callbacks = BNCustomDataRenderer {
21 context: renderer as *mut _ as *mut c_void,
22 freeObject: Some(cb_free_object::<C>),
23 isValidForData: Some(cb_is_valid_for_data::<C>),
24 getLinesForData: Some(cb_get_lines_for_data::<C>),
25 freeLines: Some(cb_free_lines),
26 };
27 let result = unsafe { BNCreateDataRenderer(&mut callbacks) };
28 let core = unsafe { CoreDataRenderer::from_raw(NonNull::new(result).unwrap()) };
29 let container = DataRendererContainer::get();
30 match C::REGISTRATION_TYPE {
31 RegistrationType::Generic => container.register_data_renderer(&core),
32 RegistrationType::Specific => container.register_specific_data_renderer(&core),
33 }
34 (renderer, core)
35}
36
37pub fn render_lines_for_data(
39 view: &BinaryView,
40 addr: u64,
41 type_: &Type,
42 prefix: Vec<InstructionTextToken>,
43 width: usize,
44 types_ctx: &[TypeContext],
45 language: Option<&str>,
46) -> Vec<DisassemblyTextLine> {
47 let bn_prefix: Vec<BNInstructionTextToken> = prefix
48 .into_iter()
49 .map(InstructionTextToken::into_raw)
50 .collect();
51 let bn_language = BnString::from(language.unwrap_or(""));
52
53 let mut count: usize = 0;
54 let lines_ptr = unsafe {
55 BNRenderLinesForData(
56 view.handle,
57 addr,
58 type_.handle,
59 bn_prefix.as_ptr(),
60 bn_prefix.len(),
61 width,
62 &mut count as *mut usize,
63 types_ctx.as_ptr() as *mut BNTypeContext,
64 types_ctx.len(),
65 bn_language.as_ptr(),
66 )
67 };
68
69 for token in bn_prefix {
70 InstructionTextToken::free_raw(token);
71 }
72
73 let lines_arr: Array<DisassemblyTextLine> = unsafe { Array::new(lines_ptr, count, ()) };
74 lines_arr.to_vec()
75}
76
77#[derive(Clone, Copy)]
78struct DataRendererContainer {
79 pub(crate) handle: *mut BNDataRendererContainer,
80}
81
82impl DataRendererContainer {
83 pub fn get() -> Self {
84 Self {
85 handle: unsafe { BNGetDataRendererContainer() },
86 }
87 }
88
89 pub fn register_data_renderer(&self, renderer: &CoreDataRenderer) {
90 unsafe { BNRegisterGenericDataRenderer(self.handle, renderer.handle.as_ptr()) };
91 }
92
93 pub fn register_specific_data_renderer(&self, renderer: &CoreDataRenderer) {
94 unsafe { BNRegisterTypeSpecificDataRenderer(self.handle, renderer.handle.as_ptr()) };
95 }
96}
97
98pub enum RegistrationType {
100 Generic,
101 Specific,
105}
106
107pub trait CustomDataRenderer: Sized + Sync + Send + 'static {
108 const REGISTRATION_TYPE: RegistrationType;
112
113 fn is_valid_for_data(
114 &self,
115 view: &BinaryView,
116 addr: u64,
117 type_: &Type,
118 types: &[TypeContext],
119 ) -> bool;
120
121 fn lines_for_data(
122 &self,
123 view: &BinaryView,
124 addr: u64,
125 type_: &Type,
126 prefix: Vec<InstructionTextToken>,
127 width: usize,
128 types_ctx: &[TypeContext],
129 language: &str,
130 ) -> Vec<DisassemblyTextLine>;
131}
132
133pub struct CoreDataRenderer {
134 pub(crate) handle: NonNull<BNDataRenderer>,
135}
136
137impl CoreDataRenderer {
138 pub(crate) unsafe fn from_raw(handle: NonNull<BNDataRenderer>) -> CoreDataRenderer {
139 Self { handle }
140 }
141}
142
143#[repr(transparent)]
147pub struct TypeContext {
148 handle: BNTypeContext,
149}
150
151impl TypeContext {
152 pub fn ty(&self) -> &Type {
154 unsafe { core::mem::transmute::<&*mut BNType, &Type>(&self.handle.type_) }
156 }
157
158 pub fn offset(&self) -> usize {
162 self.handle.offset
163 }
164}
165
166impl Debug for TypeContext {
167 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168 f.debug_struct("TypeContext")
169 .field("ty", &self.ty())
170 .field("offset", &self.offset())
171 .finish()
172 }
173}
174
175unsafe extern "C" fn cb_free_object<C: CustomDataRenderer>(ctxt: *mut c_void) {
176 let _ = Box::from_raw(ctxt as *mut C);
177}
178
179unsafe extern "C" fn cb_is_valid_for_data<C: CustomDataRenderer>(
180 ctxt: *mut c_void,
181 view: *mut BNBinaryView,
182 addr: u64,
183 type_: *mut BNType,
184 type_ctx: *mut BNTypeContext,
185 ctx_count: usize,
186) -> bool {
187 let ctxt = ctxt as *mut C;
188 let types = core::slice::from_raw_parts(type_ctx as *mut TypeContext, ctx_count);
190 (*ctxt).is_valid_for_data(
191 &BinaryView::from_raw(view),
192 addr,
193 &Type::from_raw(type_),
194 types,
195 )
196}
197
198unsafe extern "C" fn cb_get_lines_for_data<C: CustomDataRenderer>(
199 ctxt: *mut c_void,
200 view: *mut BNBinaryView,
201 addr: u64,
202 type_: *mut BNType,
203 prefix: *const BNInstructionTextToken,
204 prefix_count: usize,
205 width: usize,
206 count: *mut usize,
207 type_ctx: *mut BNTypeContext,
208 ctx_count: usize,
209 language: *const ffi::c_char,
210) -> *mut BNDisassemblyTextLine {
211 let ctxt = ctxt as *mut C;
212 let types = core::slice::from_raw_parts(type_ctx as *mut TypeContext, ctx_count);
214 let prefix = core::slice::from_raw_parts(prefix, prefix_count)
215 .iter()
216 .map(InstructionTextToken::from_raw)
217 .collect::<Vec<_>>();
218 let result = (*ctxt).lines_for_data(
219 &BinaryView::from_raw(view),
220 addr,
221 &Type::from_raw(type_),
222 prefix,
223 width,
224 types,
225 ffi::CStr::from_ptr(language).to_str().unwrap(),
226 );
227 let result: Box<[BNDisassemblyTextLine]> = result
228 .into_iter()
229 .map(DisassemblyTextLine::into_raw)
230 .collect();
231 *count = result.len();
232 Box::leak(result).as_mut_ptr()
233}
234
235unsafe extern "C" fn cb_free_lines(
236 _ctx: *mut c_void,
237 lines: *mut BNDisassemblyTextLine,
238 count: usize,
239) {
240 let lines = Box::from_raw(std::ptr::slice_from_raw_parts_mut(lines, count));
241 for line in lines {
242 let _ = DisassemblyTextLine::from_raw(&line);
243 }
244}