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