1use std::ffi::{CStr, CString};
2use std::ptr;
3
4use super::{MonoClass, mono_handle};
5use crate::{MonoError, MonoImageOpenStatus, Result, api};
6
7struct FindContext<'a> {
8 target_name: &'a str,
9 result: Option<MonoImage>,
10 api_failed: bool,
11}
12
13mono_handle!(MonoImage);
14
15impl MonoImage {
16 pub fn find(name: &str) -> Result<Option<Self>> {
22 let mut ctx = FindContext {
23 target_name: name,
24 result: None,
25 api_failed: false,
26 };
27 api()?.assembly_foreach(find_image_callback, ptr::addr_of_mut!(ctx).cast());
28 if ctx.api_failed {
29 return Err(MonoError::Uninitialized);
30 }
31 Ok(ctx.result)
32 }
33
34 pub fn open_from_data(data: &mut [u8]) -> Result<Self> {
44 let data_len = u32::try_from(data.len())
45 .map_err(|_| MonoError::ImageOpenFailed(MonoImageOpenStatus::ImageInvalid))?;
46
47 let mut status: i32 = 0;
48 let ptr = api()?.image_open_from_data(
49 data.as_mut_ptr().cast(),
50 data_len,
51 1,
52 std::ptr::addr_of_mut!(status),
53 );
54
55 let s = MonoImageOpenStatus::from_raw(status);
56 if !s.is_ok() {
57 return Err(MonoError::ImageOpenFailed(s));
58 }
59
60 MonoImage::from_ptr(ptr).ok_or(MonoError::ImageOpenFailed(
61 MonoImageOpenStatus::ImageInvalid,
62 ))
63 }
64
65 pub fn open_status_message(status: i32) -> Result<String> {
71 let ptr = api()?.image_strerror(status);
72 if ptr.is_null() {
73 return Ok("unknown status".to_owned());
74 }
75
76 Ok(unsafe { CStr::from_ptr(ptr) }
77 .to_string_lossy()
78 .into_owned())
79 }
80
81 pub fn class_from_name(self, namespace: &str, name: &str) -> Result<Option<MonoClass>> {
88 let ns = CString::new(namespace).map_err(|_| MonoError::NullByteInName)?;
89 let nm = CString::new(name).map_err(|_| MonoError::NullByteInName)?;
90 let ptr = api()?.class_from_name(self.as_ptr(), ns.as_ptr(), nm.as_ptr());
91 Ok(MonoClass::from_ptr(ptr))
92 }
93}
94
95unsafe extern "C" fn find_image_callback(assembly: *mut c_void, user_data: *mut c_void) {
102 let Ok(api) = api() else {
103 let ctx = unsafe { &mut *user_data.cast::<FindContext<'_>>() };
104 ctx.api_failed = true;
105 return;
106 };
107
108 let ctx = unsafe { &mut *user_data.cast::<FindContext<'_>>() };
109 if ctx.result.is_some() {
110 return;
111 }
112
113 let image = api.assembly_get_image(assembly);
114 if image.is_null() {
115 return;
116 }
117
118 let name_ptr = api.image_get_name(image);
119 if name_ptr.is_null() {
120 return;
121 }
122
123 let name = unsafe { CStr::from_ptr(name_ptr) }.to_str().unwrap_or("");
124 if name == ctx.target_name {
125 ctx.result = Some(unsafe { MonoImage::from_ptr_unchecked(image) });
126 }
127}