use crate::{Result, error::Error};
use serde::Serialize;
#[derive(Debug, Clone, Serialize)]
pub struct DotNetResource<'a> {
pub name: String,
#[serde(skip)]
pub flags: Vec<crate::stream::meta_data_tables::mdtables::enums::ClrManifestResourceFlags>,
pub location: ResourceLocation,
#[serde(skip)]
pub data: Option<&'a [u8]>,
}
#[derive(Debug, Clone, Serialize)]
pub enum ResourceLocation {
Embedded,
External { file: String },
Linked { assembly: String },
}
impl<'a> crate::DnPe<'a> {
pub fn resources(&self) -> Result<Vec<DotNetResource<'a>>> {
let Ok(clr) = self.net() else {
return Ok(vec![]);
};
let Ok(table) = clr.md_table("ManifestResource") else {
return Ok(vec![]);
};
let row_count = table.row_count();
let mut out = Vec::with_capacity(row_count.min(4096));
for i in 0..row_count {
let row =
table.row::<crate::stream::meta_data_tables::mdtables::ManifestResource>(i)?;
let location = if row.implementation.row_index == 0 {
ResourceLocation::Embedded
} else {
match row.implementation.table {
"File" => ResourceLocation::External {
file: self
.lookup_file_name(row.implementation.row_index)
.unwrap_or_default(),
},
"AssemblyRef" => ResourceLocation::Linked {
assembly: self
.lookup_assembly_ref_name(row.implementation.row_index)
.unwrap_or_default(),
},
_ => ResourceLocation::Embedded,
}
};
let data = if matches!(location, ResourceLocation::Embedded) {
self.read_embedded_resource(row.offset).ok()
} else {
None
};
out.push(DotNetResource {
name: row.name.clone(),
flags: row.flags.clone(),
location,
data,
});
}
Ok(out)
}
fn lookup_file_name(&self, rid: usize) -> Option<String> {
let clr = self.net().ok()?;
let table = clr.md_table("File").ok()?;
let row = table
.row::<crate::stream::meta_data_tables::mdtables::File>(rid.saturating_sub(1))
.ok()?;
Some(row.name.clone())
}
fn lookup_assembly_ref_name(&self, rid: usize) -> Option<String> {
let clr = self.net().ok()?;
let table = clr.md_table("AssemblyRef").ok()?;
let row = table
.row::<crate::stream::meta_data_tables::mdtables::AssemblyRef>(rid.saturating_sub(1))
.ok()?;
Some(row.name.clone())
}
fn read_embedded_resource(&self, manifest_offset: u32) -> Result<&'a [u8]> {
let abs = self
.resources_rva
.checked_add(manifest_offset)
.ok_or(Error::UnresolvedRvaError(manifest_offset))?;
let header: u32 = self.get_data(&abs, &4)?;
let payload_rva = abs.checked_add(4).ok_or(Error::UnresolvedRvaError(abs))?;
let max = self
.resources_size
.saturating_sub(manifest_offset.saturating_add(4));
let length = (header as usize).min(max as usize);
self.get_slice(&payload_rva, length)
}
}