mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-15 21:00:03 +02:00
rewrite mtk_bdp extractor
This commit is contained in:
@@ -116,7 +116,7 @@ pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
|||||||
let offset = sections[i as usize].offset;
|
let offset = sections[i as usize].offset;
|
||||||
|
|
||||||
println!("\n({}/{}) - {}, Size: {}",
|
println!("\n({}/{}) - {}, Size: {}",
|
||||||
item.item_id, items.len(), item.name, size);
|
i + 1, items.len(), item.name, size);
|
||||||
|
|
||||||
assert!(sections[i as usize].index == item.item_id, "Item ID in TOC does not match ID from header!");
|
assert!(sections[i as usize].index == item.item_id, "Item ID in TOC does not match ID from header!");
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ use crate::utils::msd_ouith_parser_tizen_1_9::{parse_blob_1_9};
|
|||||||
#[derive(BinRead)]
|
#[derive(BinRead)]
|
||||||
struct FileHeader {
|
struct FileHeader {
|
||||||
#[br(count = 6)] _magic_bytes: Vec<u8>,
|
#[br(count = 6)] _magic_bytes: Vec<u8>,
|
||||||
#[br(count = 12)] _unk: Vec<u8>,
|
_header_checksum: u32, //CRC32 of data with the size of "_header_size"
|
||||||
section_count: u32
|
_header_size: u64,
|
||||||
|
section_count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(BinRead)]
|
#[derive(BinRead)]
|
||||||
@@ -110,7 +111,7 @@ pub fn extract_msd11(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
|||||||
let offset = sections[i as usize].offset;
|
let offset = sections[i as usize].offset;
|
||||||
|
|
||||||
println!("\n({}/{}) - {}, Size: {}",
|
println!("\n({}/{}) - {}, Size: {}",
|
||||||
item.item_id, items.len(), item.name, size);
|
i + 1, items.len(), item.name, size);
|
||||||
|
|
||||||
assert!(sections[i as usize].index == item.item_id, "Item ID in TOC does not match ID from header!");
|
assert!(sections[i as usize].index == item.item_id, "Item ID in TOC does not match ID from header!");
|
||||||
|
|
||||||
|
|||||||
+110
-105
@@ -8,39 +8,56 @@ use std::sync::OnceLock;
|
|||||||
use crate::utils::common;
|
use crate::utils::common;
|
||||||
|
|
||||||
#[derive(BinRead)]
|
#[derive(BinRead)]
|
||||||
struct TocEntry {
|
struct PITITPITEntry {
|
||||||
id: u32,
|
nand_size: u32,
|
||||||
offset: u32,
|
pit_offset: u32,
|
||||||
size: u32,
|
pit_size: u32,
|
||||||
|
_private_data_2: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BinRead)]
|
||||||
|
struct PITITBITEntry {
|
||||||
|
bit_offset: u32,
|
||||||
|
bit_size: u32,
|
||||||
|
_private_data_1: u32,
|
||||||
|
_private_data_2: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(BinRead)]
|
||||||
|
struct PITHeader {
|
||||||
|
#[br(count = 8)] pit_magic: Vec<u8>,
|
||||||
_unk: u32,
|
_unk: u32,
|
||||||
part_type: u32,
|
first_entry_offset: u32,
|
||||||
|
entry_size: u32,
|
||||||
|
entry_count: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(BinRead)]
|
#[derive(BinRead)]
|
||||||
struct PartHeader {
|
struct PITEntry {
|
||||||
#[br(count = 20)] _unk1: Vec<u8>,
|
|
||||||
part_count: u32,
|
|
||||||
#[br(count = 40)] _unk2: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(BinRead)]
|
|
||||||
struct PartEntry {
|
|
||||||
#[br(count = 16)] name_bytes: Vec<u8>,
|
#[br(count = 16)] name_bytes: Vec<u8>,
|
||||||
id: u32,
|
partition_id: u32,
|
||||||
_unk: u32,
|
_unknown: u32,
|
||||||
_part_type: u32,
|
offset_on_nand: u32,
|
||||||
size: u32,
|
size_on_nand: u32,
|
||||||
#[br(count = 32)] _unknown: Vec<u8>,
|
#[br(count = 32)] _unused: Vec<u8>,
|
||||||
}
|
}
|
||||||
impl PartEntry {
|
impl PITEntry {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
common::string_from_bytes(&self.name_bytes)
|
common::string_from_bytes(&self.name_bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ITIP_MAGIC: [u8; 8] = [0x69, 0x54, 0x49, 0x50, 0x69, 0x54, 0x49, 0x50];
|
#[derive(BinRead)]
|
||||||
|
struct BITEntry {
|
||||||
|
partition_id: u32,
|
||||||
|
offset: u32,
|
||||||
|
size: u32,
|
||||||
|
offset_in_target_part: u32,
|
||||||
|
_unknown: u32,
|
||||||
|
}
|
||||||
|
|
||||||
static ITIP_OFFSET: OnceLock<usize> = OnceLock::new();
|
static PITIT_MAGIC: [u8; 8] = [0x69, 0x54, 0x49, 0x50, 0x69, 0x54, 0x49, 0x50];
|
||||||
|
static PITIT_OFFSET: OnceLock<usize> = OnceLock::new();
|
||||||
|
|
||||||
fn find_bytes(data: &[u8], pattern: &[u8]) -> Option<usize> {
|
fn find_bytes(data: &[u8], pattern: &[u8]) -> Option<usize> {
|
||||||
data.windows(pattern.len()).position(|window| window == pattern)
|
data.windows(pattern.len()).position(|window| window == pattern)
|
||||||
@@ -56,8 +73,8 @@ pub fn is_mtk_bdp_file(mut file: &File) -> bool {
|
|||||||
|
|
||||||
file.read_to_end(&mut data).expect("Failed to read from file.");
|
file.read_to_end(&mut data).expect("Failed to read from file.");
|
||||||
|
|
||||||
if let Some(pos) = find_bytes(&data, &ITIP_MAGIC) {
|
if let Some(pos) = find_bytes(&data, &PITIT_MAGIC) {
|
||||||
ITIP_OFFSET.set(start_offset as usize + pos).unwrap();
|
PITIT_OFFSET.set(start_offset as usize + pos).unwrap();
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
@@ -65,108 +82,96 @@ pub fn is_mtk_bdp_file(mut file: &File) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_mtk_bdp(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
pub fn extract_mtk_bdp(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
//check if its a philips file which has a 106 byte header that offsets everything
|
let offset = PITIT_OFFSET.get().unwrap();
|
||||||
let base_offset;
|
println!("\nReading PITIT at: {}", offset);
|
||||||
let magic = common::read_file(&file, 0, 7)?;
|
|
||||||
if magic == b"PHILIPS" {
|
|
||||||
base_offset = 106;
|
|
||||||
println!("Philips variant detected!\n")
|
|
||||||
} else {
|
|
||||||
base_offset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
let offset = ITIP_OFFSET.get().unwrap();
|
|
||||||
println!("Reading PIT at: {}", offset);
|
|
||||||
|
|
||||||
file.seek(SeekFrom::Start(*offset as u64 + 8))?;
|
file.seek(SeekFrom::Start(*offset as u64 + 8))?;
|
||||||
|
|
||||||
let ittp_check = common::read_exact(&mut file, 8)?;
|
let pitit_check = common::read_exact(&mut file, 8)?;
|
||||||
let mut toc_offset = 0;
|
//{UPG_INFO}the upg bin is %d version(old is 0,new is 1)!\n
|
||||||
if ittp_check == ITIP_MAGIC {
|
let pitit_ver = if pitit_check == PITIT_MAGIC {0} else {1};
|
||||||
//old pit
|
|
||||||
for i in 0..10 {
|
|
||||||
let line = common::read_exact(&mut file, 16)?;
|
|
||||||
//line that ends the old PIT, TOC appears directly after
|
|
||||||
if line == b"\x50\x49\x54\x69\x50\x49\x54\x69\x50\x49\x54\x69\x50\x49\x54\x69" {
|
|
||||||
toc_offset = offset + (16* (i + 2));
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if toc_offset == 0 {
|
|
||||||
println!("Failed to find TOC!");
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//new pit
|
|
||||||
let _ = common::read_exact(&mut file, 16)?;
|
|
||||||
let toc_offset_bytes = common::read_exact(&mut file, 4)?;
|
|
||||||
toc_offset = u32::from_le_bytes(toc_offset_bytes.try_into().unwrap()) as usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("\nReading TOC at: {}", toc_offset);
|
let mut pit_offset: u64 = 0;
|
||||||
file.seek(SeekFrom::Start(base_offset + toc_offset as u64))?;
|
let mut bit_offset: u64 = 0;
|
||||||
|
|
||||||
let toc_check = common::read_exact(&mut file, 20)?;
|
|
||||||
if toc_check != b"\xCD\xAB\x30\x85\xCD\xAB\x30\x85\xCD\xAB\x30\x85\xCD\xAB\x30\x85\xCD\xAB\x30\x85" {
|
|
||||||
println!("Invalid TOC!");
|
|
||||||
return Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut entries: Vec<TocEntry> = Vec::new();
|
|
||||||
let mut part_table_offset: Option<u64> = None;
|
|
||||||
let mut n = 1;
|
|
||||||
loop {
|
loop {
|
||||||
let entry: TocEntry = file.read_le()?;
|
let pitit_pit_entry: PITITPITEntry = file.read_le()?;
|
||||||
if entry.id == 0x8530efef {
|
if pitit_pit_entry.nand_size == 0x69544950 {break}; //PITi - end marker of PITIT
|
||||||
break
|
if pitit_ver == 1 {
|
||||||
|
//old PITIT does not have BIT entry, because BIT appears directly after PITIT
|
||||||
|
let pitit_bit_entry: PITITBITEntry = file.read_le()?;
|
||||||
|
println!("PITIT Entry - NAND Size: {}, PIT Offset: {}, PIT Size: {}, BIT Offset: {}, BIT Size: {}",
|
||||||
|
pitit_pit_entry.nand_size, pitit_pit_entry.pit_offset, pitit_pit_entry.pit_size, pitit_bit_entry.bit_offset, pitit_bit_entry.bit_size);
|
||||||
|
if bit_offset == 0 { bit_offset = pitit_bit_entry.bit_offset as u64 } //use the first entry in PITIT
|
||||||
|
} else {
|
||||||
|
println!("PITIT Entry - NAND Size: {}, PIT Offset: {}, PIT Size: {}",
|
||||||
|
pitit_pit_entry.nand_size, pitit_pit_entry.pit_offset, pitit_pit_entry.pit_size);
|
||||||
}
|
}
|
||||||
println!("Entry {}. ID: {:02x}, Offset: {}, Size: {}, Type: {:02x}", n, entry.id, entry.offset, entry.size, entry.part_type);
|
if pit_offset == 0 { pit_offset = pitit_pit_entry.pit_offset as u64 } //use the first entry in PITIT
|
||||||
|
}
|
||||||
if entry.id == 0x02 && entry.part_type == 0x00 {
|
if pitit_ver == 0 && bit_offset == 0{
|
||||||
part_table_offset = Some(entry.offset as u64);
|
//in old upg bin, BIT appears directly after PITIT. so we can use the current file pos cuz we just ended reading PITIT
|
||||||
|
bit_offset = file.stream_position()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.push(entry);
|
println!("\nReading PIT at: {}", pit_offset); //PIT is the NAND partition table.
|
||||||
n += 1;
|
file.seek(SeekFrom::Start(pit_offset))?;
|
||||||
|
let mut pit_entries: Vec<PITEntry> = Vec::new();
|
||||||
|
let pit_header: PITHeader = file.read_le()?;
|
||||||
|
if pit_header.pit_magic != b"\xDC\xEA\x30\x85\xDC\xEA\x30\x85" {
|
||||||
|
println!("Invalid PIT Magic!");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
println!("PIT Info - First entry offs: {}, Entry size: {}, Entry count: {}", pit_header.first_entry_offset, pit_header.entry_size, pit_header.entry_count);
|
||||||
|
file.seek(SeekFrom::Start(pit_offset + pit_header.first_entry_offset as u64))?;
|
||||||
|
|
||||||
|
for i in 0..pit_header.entry_count {
|
||||||
|
let pit_entry: PITEntry = file.read_le()?;
|
||||||
|
println!("{}. ID: {:02x}, Name: {}, NAND Offset: {}, NAND Size: {}",
|
||||||
|
i + 1, pit_entry.partition_id, pit_entry.name(), pit_entry.offset_on_nand, pit_entry.size_on_nand);
|
||||||
|
pit_entries.push(pit_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
if part_table_offset.is_none() {
|
println!("\nReading BIT at: {}", bit_offset); //BIT is the table of objects present in the update file.
|
||||||
println!("Failed to find partition table offset!");
|
file.seek(SeekFrom::Start(bit_offset))?;
|
||||||
|
let mut bit_entries: Vec<BITEntry> = Vec::new();
|
||||||
|
let bit_magic = common::read_exact(&mut file, 20)?;
|
||||||
|
if bit_magic != b"\xCD\xAB\x30\x85\xCD\xAB\x30\x85\xCD\xAB\x30\x85\xCD\xAB\x30\x85\xCD\xAB\x30\x85" {
|
||||||
|
println!("Invalid BIT Magic!");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("\nReading partition table at: {}", part_table_offset.unwrap());
|
let mut bit_i = 0;
|
||||||
|
loop {
|
||||||
|
let bit_entry: BITEntry = file.read_le()?;
|
||||||
|
if bit_entry.partition_id == 0x8530EFEF {break}; //EF EF 30 85 - end marker of BIT
|
||||||
|
println!("{}. ID: {:02x}, Offset: {}, Size: {}, Offset in part: {}",
|
||||||
|
bit_i + 1, bit_entry.partition_id, bit_entry.offset, bit_entry.size, bit_entry.offset_in_target_part);
|
||||||
|
bit_entries.push(bit_entry);
|
||||||
|
bit_i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
file.seek(SeekFrom::Start(base_offset + part_table_offset.unwrap()))?;
|
//extraction logic
|
||||||
let part_header: PartHeader = file.read_le()?;
|
for (i, bit_entry) in bit_entries.iter().enumerate() {
|
||||||
println!("Part count: {}", part_header.part_count);
|
//find the name of partition using PIT by partition_id. if not found use the ID as placeholder
|
||||||
|
let mut name = format!("unknown_{:02x}", bit_entry.partition_id);
|
||||||
|
for pit_entry in &pit_entries {
|
||||||
|
if pit_entry.partition_id == bit_entry.partition_id {
|
||||||
|
name = pit_entry.name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for i in 0..part_header.part_count {
|
println!("\n({}/{}) - {}, Offset: {}, Size: {}, Offset in partition: {}",
|
||||||
let part_entry: PartEntry = file.read_le()?;
|
i + 1, bit_entries.len(), name, bit_entry.offset, bit_entry.size, bit_entry.offset_in_target_part);
|
||||||
println!("\n({}/{}) - {}, ID: {:02x}, Size: {}", i + 1, part_header.part_count, part_entry.name(), part_entry.id, part_entry.size);
|
|
||||||
|
|
||||||
for entry in &entries{
|
let data = common::read_file(&file, bit_entry.offset as u64, bit_entry.size as usize)?;
|
||||||
if entry.id == part_entry.id && entry.size == part_entry.size {
|
|
||||||
//println!("- Saving {}.bin, Offset: {}, Size: {}", part_entry.name(), entry.offset, entry.size);
|
|
||||||
let current_pos = file.stream_position()?;
|
|
||||||
file.seek(SeekFrom::Start(base_offset + entry.offset as u64))?;
|
|
||||||
let data = common::read_exact(&mut file, entry.size as usize)?;
|
|
||||||
|
|
||||||
let output_path = Path::new(&output_folder).join(part_entry.name() + ".bin");
|
|
||||||
|
|
||||||
|
let output_path = Path::new(&output_folder).join(format!("{}.bin", name));
|
||||||
fs::create_dir_all(&output_folder)?;
|
fs::create_dir_all(&output_folder)?;
|
||||||
let mut out_file = OpenOptions::new()
|
let mut out_file = OpenOptions::new().read(true).write(true).create(true).open(output_path)?;
|
||||||
.write(true)
|
out_file.seek(SeekFrom::Start(bit_entry.offset_in_target_part as u64))?;
|
||||||
.create(true)
|
|
||||||
.open(output_path)?;
|
|
||||||
|
|
||||||
out_file.write_all(&data)?;
|
out_file.write_all(&data)?;
|
||||||
|
|
||||||
println!("- Saved file!");
|
println!("-- Saved file!");
|
||||||
file.seek(SeekFrom::Start(current_pos))?;
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("\nExtraction finished!");
|
println!("\nExtraction finished!");
|
||||||
|
|||||||
Reference in New Issue
Block a user