use tracing::{debug, warn};
use crate::{IdeviceError, services::restore::RestoreError};
const MBN_V1_MAGIC: &[u8] = &[0x0a, 0x00, 0x00, 0x00];
const MBN_V2_MAGIC: &[u8] = &[0xd1, 0xdc, 0x4b, 0x84, 0x34, 0x10, 0xd7, 0x73];
const MBN_BIN_MAGIC: &[u8] = &[0x04, 0x00, 0xea, 0x6c, 0x69, 0x48, 0x55];
const MBN_BIN_MAGIC_OFFSET: usize = 1;
const MBN_V1_HEADER: usize = 40; const MBN_V2_HEADER: usize = 56;
fn read_u32_le(data: &[u8], offset: usize) -> Option<u32> {
data.get(offset..offset + 4)
.map(|b| u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
}
fn detect_image_size(data: &[u8]) -> Option<usize> {
if data.len() > MBN_V2_MAGIC.len() && data.starts_with(MBN_V2_MAGIC) {
let data_size = read_u32_le(data, 10 * 4)? as usize;
Some(data_size + MBN_V2_HEADER)
} else if data.len() > MBN_V1_MAGIC.len() && data.starts_with(MBN_V1_MAGIC) {
let data_size = read_u32_le(data, 8 * 4)? as usize;
Some(data_size + MBN_V1_HEADER)
} else if data.len() > MBN_BIN_MAGIC.len() + MBN_BIN_MAGIC_OFFSET
&& data[MBN_BIN_MAGIC_OFFSET..MBN_BIN_MAGIC_OFFSET + MBN_BIN_MAGIC.len()] == *MBN_BIN_MAGIC
{
read_u32_le(data, 4).map(|s| s as usize)
} else {
None
}
}
pub fn mbn_stitch(data: &[u8], blob: &[u8]) -> Result<Vec<u8>, IdeviceError> {
if data.is_empty() {
return Err(IdeviceError::Restore(RestoreError::Baseband(
"mbn_stitch: data is empty".into(),
)));
}
if blob.is_empty() {
return Err(IdeviceError::Restore(RestoreError::Baseband(
"mbn_stitch: blob is empty".into(),
)));
}
if blob.len() > data.len() {
return Err(IdeviceError::Restore(RestoreError::Baseband(format!(
"mbn_stitch: blob ({}) larger than data ({})",
blob.len(),
data.len()
))));
}
if let Some(parsed) = detect_image_size(data)
&& parsed != data.len()
{
warn!(
"mbn_stitch: size mismatch, header says {parsed:#x}, input is {:#x}",
data.len()
);
}
let stitch_offset = data.len() - blob.len();
debug!(
"mbn_stitch: stitching at {stitch_offset:#x}, size {:#x}",
blob.len()
);
let mut out = data.to_vec();
out[stitch_offset..].copy_from_slice(blob);
Ok(out)
}