mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-15 21:00:03 +02:00
Add amlogic image extractor, sparse: add support for FILL chunk.
This commit is contained in:
@@ -14,6 +14,7 @@ pub mod funai_upg;
|
||||
pub mod pana_dvd;
|
||||
pub mod android_ota_payload;
|
||||
pub mod bdl;
|
||||
pub mod amlogic;
|
||||
|
||||
pub mod pup;
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
use std::fs::File;
|
||||
use std::path::{Path};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write, Seek, SeekFrom};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
|
||||
use crate::utils::common;
|
||||
use crate::utils::sparse::{unsparse_to_file};
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct ImageHeader {
|
||||
_crc32: u32,
|
||||
version: u32,
|
||||
#[br(count = 4)] _magic_bytes: Vec<u8>, //56 19 B5 27
|
||||
image_size: u64,
|
||||
item_align_size: u32,
|
||||
item_count: u32,
|
||||
#[br(count = 36)] _reserved: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
struct ItemEntry {
|
||||
_item_id: u32,
|
||||
file_type: u32,
|
||||
_current_offset_in_item: u64,
|
||||
offset_in_image: u64,
|
||||
item_size: u64,
|
||||
#[br(count = 256)] item_type_bytes: Vec<u8>,
|
||||
#[br(count = 256)] name_bytes: Vec<u8>,
|
||||
_verify: u32,
|
||||
_is_backup_item: u16,
|
||||
_backup_item_id: u16,
|
||||
#[br(count = 24)] _reserved: Vec<u8>,
|
||||
}
|
||||
impl ItemEntry {
|
||||
fn item_type(&self) -> String {
|
||||
common::string_from_bytes(&self.item_type_bytes)
|
||||
}
|
||||
fn name(&self) -> String {
|
||||
common::string_from_bytes(&self.name_bytes)
|
||||
}
|
||||
fn is_sparse(&self) -> bool {
|
||||
self.file_type == 254
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_amlogic_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 8, 4).expect("Failed to read from file.");
|
||||
if header == b"\x56\x19\xB5\x27" {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_amlogic(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
file.seek(SeekFrom::Start(0))?;
|
||||
let header: ImageHeader = file.read_le()?;
|
||||
|
||||
println!("File info -\nImage size: {}\nItem align size: {}\nItem count: {}\nFormat version: {}",
|
||||
header.image_size, header.item_align_size, header.item_count, header.version);
|
||||
|
||||
if header.version != 2 {
|
||||
println!("\nSorry, this format version is not yet supported!");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut items: Vec<ItemEntry> = Vec::new();
|
||||
|
||||
for _i in 0..header.item_count {
|
||||
let item: ItemEntry = file.read_le()?;
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
for (i, item) in items.iter().enumerate() {
|
||||
println!("\n({}/{}) - {}, Type: {}, Offset: {}, Size: {} {}",
|
||||
i+1, header.item_count, item.name(), item.item_type(), item.offset_in_image, item.item_size, if item.is_sparse() {"[SPARSE]"} else {""});
|
||||
|
||||
if item.item_type() == "VERIFY" { //verify item is SHA1 of partition item
|
||||
let sum_bytes = common::read_file(&file, item.offset_in_image, item.item_size as usize)?;
|
||||
let sum = common::string_from_bytes(&sum_bytes);
|
||||
println!("- Checksum for {}: {}", item.name(), sum);
|
||||
|
||||
} else {
|
||||
let data = common::read_file(&file, item.offset_in_image, item.item_size as usize)?;
|
||||
|
||||
let extension = if item.item_type() == "PARTITION" {"img"} else {&item.item_type()};
|
||||
let output_path = Path::new(&output_folder).join(format!("{}.{}", item.name(), extension));
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
|
||||
if item.is_sparse() {
|
||||
println!("- Unsparsing...");
|
||||
unsparse_to_file(&data, output_path)?;
|
||||
println!("-- Saved file!");
|
||||
continue
|
||||
|
||||
} else {
|
||||
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&data)?;
|
||||
println!("- Saved file!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
println!("\nExtraction finished!");
|
||||
Ok(())
|
||||
}
|
||||
+5
-1
@@ -115,7 +115,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
else if formats::pfl_upg::is_pfl_upg_file(&file) {
|
||||
println!("PFL UPG file detected!");
|
||||
formats::pfl_upg::extract_pfl_upg(&file, &output_path)?;
|
||||
}
|
||||
}
|
||||
else if formats::amlogic::is_amlogic_file(&file) {
|
||||
println!("Amlogic image file detected!");
|
||||
formats::amlogic::extract_amlogic(&file, &output_path)?;
|
||||
}
|
||||
else if formats::pana_dvd::is_pana_dvd_file(&file) {
|
||||
println!("PANA_DVD file detected!");
|
||||
formats::pana_dvd::extract_pana_dvd(&file, &output_path)?;
|
||||
|
||||
@@ -41,6 +41,16 @@ pub fn unsparse_to_file(data: &[u8], file_path: PathBuf) -> Result<(), Box<dyn s
|
||||
|
||||
if chunk_header.chunk_type == 0xCAC1 { //"raw" type chunk (actual data)
|
||||
out_file.write_all(&chunk_data)?;
|
||||
|
||||
} else if chunk_header.chunk_type == 0xCAC2 { // "fill" type chunk (fill size with a value)
|
||||
if chunk_data.len() != 4 {
|
||||
return Err("Inavlid lenght of FILL chunk!".into());
|
||||
}
|
||||
let fill_size = (chunk_header.chunk_size * file_header.block_size) / 4;
|
||||
let fill_data = chunk_data.repeat(fill_size as usize);
|
||||
|
||||
out_file.write_all(&fill_data)?;
|
||||
|
||||
} else if chunk_header.chunk_type == 0xCAC3 { // "dont care" type chunk (skip over)
|
||||
let skip_size = file_header.block_size as u64 * chunk_header.chunk_size as u64;
|
||||
let current_pos = out_file.stream_position()?;
|
||||
|
||||
Reference in New Issue
Block a user