mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-15 21:00:03 +02:00
testing modular format system
This commit is contained in:
+43
-2
@@ -1,5 +1,14 @@
|
||||
use std::any::Any;
|
||||
use crate::ProgramContext;
|
||||
|
||||
pub struct Format {
|
||||
pub name: &'static str,
|
||||
pub detect_func: fn(&ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>>,
|
||||
pub run_func: fn(&ProgramContext, Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>>,
|
||||
}
|
||||
|
||||
pub mod mstar;
|
||||
pub mod samsung_old;
|
||||
//pub mod samsung_old; not sure what to do with this
|
||||
pub mod nvt_timg;
|
||||
pub mod pfl_upg;
|
||||
pub mod sddl_sec;
|
||||
@@ -31,4 +40,36 @@ pub mod epk3;
|
||||
pub mod mtk_pkg;
|
||||
pub mod mtk_pkg_old;
|
||||
pub mod mtk_pkg_new;
|
||||
pub mod mtk_bdp;
|
||||
pub mod mtk_bdp;
|
||||
|
||||
pub fn get_registry() -> Vec<Format> {
|
||||
return vec![
|
||||
crate::formats::amlogic::format(),
|
||||
crate::formats::epk1::format(),
|
||||
crate::formats::android_ota_payload::format(),
|
||||
crate::formats::bdl::format(),
|
||||
crate::formats::epk2::format(),
|
||||
crate::formats::epk::format(),
|
||||
crate::formats::epk2b::format(),
|
||||
crate::formats::funai_upg::format(),
|
||||
crate::formats::invincible_image::format(),
|
||||
crate::formats::msd10::format(),
|
||||
crate::formats::msd11::format(),
|
||||
crate::formats::mstar::format(),
|
||||
crate::formats::novatek::format(),
|
||||
crate::formats::nvt_timg::format(),
|
||||
crate::formats::pfl_upg::format(),
|
||||
crate::formats::pup::format(),
|
||||
crate::formats::roku::format(),
|
||||
crate::formats::ruf::format(),
|
||||
crate::formats::rvp::format(),
|
||||
crate::formats::sddl_sec::format(),
|
||||
crate::formats::slp::format(),
|
||||
crate::formats::mtk_pkg_old::format(),
|
||||
crate::formats::mtk_pkg::format(),
|
||||
crate::formats::sony_bdp::format(),
|
||||
crate::formats::mtk_pkg_new::format(),
|
||||
crate::formats::pana_dvd::format(),
|
||||
crate::formats::mtk_bdp::format(),
|
||||
]
|
||||
}
|
||||
+14
-8
@@ -1,4 +1,9 @@
|
||||
use std::fs::File;
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "amlogic", detect_func: is_amlogic_file, run_func: extract_amlogic }
|
||||
}
|
||||
|
||||
use std::path::{Path};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write, Seek, SeekFrom};
|
||||
@@ -44,16 +49,17 @@ impl ItemEntry {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_amlogic_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 8, 4).expect("Failed to read from file.");
|
||||
pub fn is_amlogic_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 8, 4)?;
|
||||
if header == b"\x56\x19\xB5\x27" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_amlogic(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_amlogic(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
file.seek(SeekFrom::Start(0))?;
|
||||
let header: ImageHeader = file.read_le()?;
|
||||
|
||||
@@ -85,8 +91,8 @@ pub fn extract_amlogic(mut file: &File, output_folder: &str) -> Result<(), Box<d
|
||||
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)?;
|
||||
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.{}", item.name(), extension));
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
|
||||
if item.is_sparse() {
|
||||
println!("- Unsparsing...");
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "android_ota_payload", detect_func: is_android_ota_payload_file, run_func: extract_android_ota_payload }
|
||||
}
|
||||
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::path::{Path};
|
||||
use std::io::{Write};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
@@ -16,16 +22,17 @@ struct Header {
|
||||
metadata_signature_size: u32,
|
||||
}
|
||||
|
||||
pub fn is_android_ota_payload_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 4).expect("Failed to read from file.");
|
||||
pub fn is_android_ota_payload_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 4)?;
|
||||
if header == b"CrAU" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_android_ota_payload(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_android_ota_payload(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let header: Header = file.read_be()?;
|
||||
println!("File info:\nFormat version: {}\nManifest size: {}", header.file_format_version, header.manifest_size);
|
||||
|
||||
@@ -75,8 +82,8 @@ pub fn extract_android_ota_payload(mut file: &File, output_folder: &str) -> Resu
|
||||
break
|
||||
}
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let output_path = Path::new(&output_folder).join(format!("{}.bin", partition.partition_name));
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", partition.partition_name));
|
||||
let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&out_data)?;
|
||||
}
|
||||
|
||||
+13
-7
@@ -1,4 +1,9 @@
|
||||
use std::fs::File;
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "bdl", detect_func: is_bdl_file, run_func: extract_bdl }
|
||||
}
|
||||
|
||||
use std::path::{Path};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write, Seek, SeekFrom};
|
||||
@@ -80,16 +85,17 @@ impl PkgEntry {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_bdl_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 4).expect("Failed to read from file.");
|
||||
pub fn is_bdl_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 4)?;
|
||||
if header == b"ibdl" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_bdl(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_bdl(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let header: BdlHeader = file.read_le()?;
|
||||
|
||||
println!("File info:\nPackage count: {}\nDate: {}\nManufacturer: {}\nModel: {}\nVersion: {}\nInfo: {}",
|
||||
@@ -116,7 +122,7 @@ pub fn extract_bdl(mut file: &File, output_folder: &str) -> Result<(), Box<dyn s
|
||||
pkg_entries.push(pkg_entry);
|
||||
}
|
||||
|
||||
let pkg_folder = Path::new(&output_folder).join(pkg_header.name());
|
||||
let pkg_folder = Path::new(app_ctx.output_dir).join(pkg_header.name());
|
||||
fs::create_dir_all(&pkg_folder)?;
|
||||
|
||||
for (i, pkg_entry) in pkg_entries.iter().enumerate() {
|
||||
|
||||
+26
-17
@@ -1,30 +1,38 @@
|
||||
use std::fs::File;
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "epk", detect_func: is_epk_file, run_func: extract_epk }
|
||||
}
|
||||
|
||||
use crate::utils::common;
|
||||
use crate::formats;
|
||||
|
||||
pub fn is_epk_file(file: &File) -> bool {
|
||||
let versions = common::read_file(&file, 1712, 36).expect("Failed to read from file.");
|
||||
pub struct EpkContext {
|
||||
epk_version: u8,
|
||||
}
|
||||
|
||||
if check_epk_version(&versions).is_some() {
|
||||
true
|
||||
pub fn is_epk_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let versions = common::read_file(app_ctx.file, 1712, 36)?;
|
||||
|
||||
if let Some(epk_version) = check_epk_version(&versions) {
|
||||
Ok(Some(Box::new(EpkContext {epk_version})))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn check_epk_version(versions: &[u8]) -> Option<String> {
|
||||
fn check_epk_version(versions: &[u8]) -> Option<u8> {
|
||||
// _ - 0x00 X - a number . - a dot
|
||||
let epk2_pattern = "____XXXX.XXXX.XXXX__XX.XX.XXX_______";
|
||||
let epk3_pattern = "____X.X.X___________X.X.X___________";
|
||||
let epk3_new_pattern = "____XX.X.X__________XX.X.X__________";
|
||||
|
||||
if match_with_pattern(&versions, epk2_pattern) {
|
||||
Some("epk2".to_string())
|
||||
Some(2)
|
||||
} else if match_with_pattern(&versions, epk3_pattern) {
|
||||
Some("epk3".to_string())
|
||||
Some(3)
|
||||
} else if match_with_pattern(&versions, epk3_new_pattern) {
|
||||
Some("epk3".to_string())
|
||||
Some(3)
|
||||
}else {
|
||||
None
|
||||
}
|
||||
@@ -42,20 +50,21 @@ fn match_with_pattern(data: &[u8], pattern: &str) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
pub fn extract_epk(file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let versions = common::read_file(&file, 1712, 36)?;
|
||||
let epk_version = check_epk_version(&versions);
|
||||
pub fn extract_epk(app_ctx: &ProgramContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let ctx = ctx.and_then(|c| c.downcast::<EpkContext>().ok()).ok_or("Context is invalid or missing!")?;
|
||||
|
||||
let versions = common::read_file(app_ctx.file, 1712, 36)?;
|
||||
|
||||
let platform_version = common::string_from_bytes(&versions[4..20]);
|
||||
let sdk_version = common::string_from_bytes(&versions[20..36]);
|
||||
println!("Platform version: {}\nSDK version: {}", platform_version, sdk_version);
|
||||
|
||||
if epk_version == Some("epk2".to_string()) {
|
||||
if ctx.epk_version == 2 {
|
||||
println!("EPK2 detected!\n");
|
||||
formats::epk2::extract_epk2(file, output_folder)?;
|
||||
} else if epk_version == Some("epk3".to_string()) {
|
||||
formats::epk2::extract_epk2(app_ctx, None)?;
|
||||
} else if ctx.epk_version == 3 {
|
||||
println!("EPK3 detected!\n");
|
||||
formats::epk3::extract_epk3(file, output_folder)?;
|
||||
formats::epk3::extract_epk3(app_ctx, None)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
+15
-9
@@ -1,4 +1,9 @@
|
||||
use std::fs::File;
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "epk1", detect_func: is_epk1_file, run_func: extract_epk1 }
|
||||
}
|
||||
|
||||
use std::path::{Path};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write, Seek, SeekFrom};
|
||||
@@ -35,17 +40,18 @@ struct Pak {
|
||||
size : u32,
|
||||
}
|
||||
|
||||
pub fn is_epk1_file(file: &File) -> bool {
|
||||
let epk2_magic = common::read_file(&file, 12, 4).expect("Failed to read from file."); //for epk2b
|
||||
let epak_magic = common::read_file(&file, 0, 4).expect("Failed to read from file.");
|
||||
pub fn is_epk1_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let epk2_magic = common::read_file(app_ctx.file, 12, 4)?; //for epk2b
|
||||
let epak_magic = common::read_file(app_ctx.file, 0, 4)?;
|
||||
if epak_magic == b"epak" && epk2_magic != b"EPK2" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_epk1(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_epk1(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
//check type of epk1
|
||||
let epk1_type;
|
||||
let init_pak_count_bytes = common::read_file(&file, 8, 4)?;
|
||||
@@ -119,8 +125,8 @@ pub fn extract_epk1(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
|
||||
let data = common::read_exact(&mut file, pak_header.image_size as usize)?;
|
||||
|
||||
let output_path = Path::new(&output_folder).join(pak_header.pak_name() + ".bin");
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let output_path = Path::new(app_ctx.output_dir).join(pak_header.pak_name() + ".bin");
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&data)?;
|
||||
|
||||
|
||||
+15
-8
@@ -1,4 +1,10 @@
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "epk2", detect_func: is_epk2_file, run_func: extract_epk2 }
|
||||
}
|
||||
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::path::{Path};
|
||||
use std::io::{Write, Seek, SeekFrom, Cursor};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
@@ -65,16 +71,17 @@ struct Pak {
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub fn is_epk2_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 128, 4).expect("Failed to read from file.");
|
||||
pub fn is_epk2_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 128, 4)?;
|
||||
if header == b"epak" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_epk2(app_ctx: &ProgramContext, _: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?;
|
||||
|
||||
let stored_header = common::read_exact(&mut file, 1584)?; //max header size
|
||||
@@ -184,8 +191,8 @@ pub fn extract_epk2(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
let segment_data = common::read_exact(&mut file, actual_segment_size as usize)?;
|
||||
let out_data = decrypt_aes_ecb_auto(&matching_key_bytes, &segment_data)?;
|
||||
|
||||
let output_path = Path::new(&output_folder).join(format!("{}.bin", pak.name));
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", pak.name));
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&out_data)?;
|
||||
|
||||
|
||||
+15
-9
@@ -1,4 +1,9 @@
|
||||
use std::fs::File;
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "epk2b", detect_func: is_epk2b_file, run_func: extract_epk2b }
|
||||
}
|
||||
|
||||
use std::path::{Path};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write, Seek, SeekFrom};
|
||||
@@ -51,17 +56,18 @@ struct Pak {
|
||||
size : u32,
|
||||
}
|
||||
|
||||
pub fn is_epk2b_file(file: &File) -> bool {
|
||||
let epk2_magic = common::read_file(&file, 12, 4).expect("Failed to read from file.");
|
||||
let epak_magic = common::read_file(&file, 0, 4).expect("Failed to read from file.");
|
||||
pub fn is_epk2b_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let epk2_magic = common::read_file(app_ctx.file, 12, 4)?;
|
||||
let epak_magic = common::read_file(app_ctx.file, 0, 4)?;
|
||||
if epak_magic == b"epak" && epk2_magic == b"EPK2" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_epk2b(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_epk2b(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let header: EpkHeader = file.read_le()?;
|
||||
|
||||
println!("EPK info -\nData size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
|
||||
@@ -108,8 +114,8 @@ pub fn extract_epk2b(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
pak_header.segment_size
|
||||
};
|
||||
|
||||
let output_path = Path::new(&output_folder).join(format!("{}.bin", pak_header.pak_name()));
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", pak_header.pak_name()));
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&out_data[..segment_limit as usize])?;
|
||||
|
||||
|
||||
+8
-4
@@ -1,4 +1,7 @@
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext};
|
||||
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::path::{Path};
|
||||
use std::io::{Write, Seek, SeekFrom, Cursor};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
@@ -70,7 +73,8 @@ impl PkgInfoEntry {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_epk3(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_epk3(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
file.seek(SeekFrom::Start(0))?;
|
||||
let stored_header = common::read_exact(&mut file, 1712)?;
|
||||
let header: Vec<u8>;
|
||||
@@ -153,8 +157,8 @@ pub fn extract_epk3(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
let encrypted_data = common::read_exact(&mut file, entry.segment_size as usize + extra_segment_size)?;
|
||||
let out_data = decrypt_aes_ecb_auto(matching_key_bytes, &encrypted_data)?;
|
||||
|
||||
let output_path = Path::new(&output_folder).join(format!("{}.bin", entry.package_name()));
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", entry.package_name()));
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&out_data[extra_segment_size..])?;
|
||||
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
use std::fs::File;
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "funai_upg", detect_func: is_funai_upg_file, run_func: extract_funai_upg }
|
||||
}
|
||||
|
||||
use std::path::Path;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write};
|
||||
@@ -20,16 +25,17 @@ struct Entry {
|
||||
_unk: u16,
|
||||
}
|
||||
|
||||
pub fn is_funai_upg_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 6).expect("Failed to read from file.");
|
||||
pub fn is_funai_upg_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 6)?;
|
||||
if header == b"UPG\x00\x00\x00" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_funai_upg(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_funai_upg(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let header: Header = file.read_le()?;
|
||||
println!("File info:\nFile size: {}\nEntry count: {}", header.file_size, header.entry_count);
|
||||
|
||||
@@ -45,9 +51,9 @@ pub fn extract_funai_upg(mut file: &File, output_folder: &str) -> Result<(), Box
|
||||
println!("Descriptor entry info:\n{}", entry_string);
|
||||
}
|
||||
|
||||
let output_path = Path::new(&output_folder).join(format!("{}_{}.bin", i + 1, entry.entry_type));
|
||||
let output_path = Path::new(app_ctx.output_dir).join(format!("{}_{}.bin", i + 1, entry.entry_type));
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&data)?;
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "invincible_image", detect_func: is_invincible_image_file, run_func: extract_invincible_image }
|
||||
}
|
||||
|
||||
use std::path::{Path};
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write, Read, Seek, SeekFrom, Cursor};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
|
||||
@@ -56,16 +62,17 @@ impl Entry {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_invincible_image_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 16).expect("Failed to read from file.");
|
||||
pub fn is_invincible_image_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 16)?;
|
||||
if header == b"INVINCIBLE_IMAGE" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_invincible_image(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_invincible_image(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let header: Header = file.read_le()?;
|
||||
|
||||
println!("File info:\nFile Version: {}.{}\nVersion(1): {}\nVersion(2): {}\nVersion(3): {}\nVersion(4): {}\nData size: {}\nData start offset: {}\nKeep data size: {}\nSkip data size: {}\n\nPayload Count: {}",
|
||||
@@ -113,9 +120,9 @@ pub fn extract_invincible_image(mut file: &File, output_folder: &str) -> Result<
|
||||
println!("\n({}/{}) - {}, Size: {}", i, header.payload_count, entry.name(), entry.size);
|
||||
let data = common::read_exact(&mut data_reader, entry.size as usize)?;
|
||||
|
||||
let output_path = Path::new(&output_folder).join(entry.name() + ".bin");
|
||||
let output_path = Path::new(app_ctx.output_dir).join(entry.name() + ".bin");
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
|
||||
+17
-10
@@ -1,4 +1,10 @@
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "msd10", detect_func: is_msd10_file, run_func: extract_msd10 }
|
||||
}
|
||||
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::path::{Path};
|
||||
use std::io::{Write, Seek, SeekFrom};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
@@ -41,16 +47,17 @@ struct Section {
|
||||
size: u32,
|
||||
}
|
||||
|
||||
pub fn is_msd10_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 6).expect("Failed to read from file.");
|
||||
pub fn is_msd10_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 6)?;
|
||||
if header == b"MSDU10" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_msd10(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let header: FileHeader = file.read_le()?;
|
||||
println!("\nNumber of sections: {}", header.section_count);
|
||||
|
||||
@@ -131,8 +138,8 @@ pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
out_data = stored_data;
|
||||
}
|
||||
|
||||
let output_path = Path::new(&output_folder).join(item.name.clone());
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let output_path = Path::new(app_ctx.output_dir).join(item.name.clone());
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&out_data)?;
|
||||
|
||||
@@ -176,8 +183,8 @@ pub fn extract_msd10(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
out_data = stored_data;
|
||||
}
|
||||
|
||||
let output_path = Path::new(&output_folder).join(item.name.clone());
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let output_path = Path::new(app_ctx.output_dir).join(item.name.clone());
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&out_data)?;
|
||||
|
||||
|
||||
+15
-8
@@ -1,4 +1,10 @@
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "msd11", detect_func: is_msd11_file, run_func: extract_msd11 }
|
||||
}
|
||||
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::path::{Path};
|
||||
use std::io::{Write};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
@@ -42,16 +48,17 @@ struct Section {
|
||||
size: u64,
|
||||
}
|
||||
|
||||
pub fn is_msd11_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 6).expect("Failed to read from file.");
|
||||
pub fn is_msd11_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 6)?;
|
||||
if header == b"MSDU11" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_msd11(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_msd11(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let header: FileHeader = file.read_le()?;
|
||||
println!("\nNumber of sections: {}", header.section_count);
|
||||
|
||||
@@ -126,8 +133,8 @@ pub fn extract_msd11(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
out_data = stored_data;
|
||||
}
|
||||
|
||||
let output_path = Path::new(&output_folder).join(item.name.clone());
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let output_path = Path::new(app_ctx.output_dir).join(item.name.clone());
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&out_data)?;
|
||||
|
||||
|
||||
+15
-15
@@ -1,4 +1,10 @@
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "mstar", detect_func: is_mstar_file, run_func: extract_mstar }
|
||||
}
|
||||
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::path::{Path};
|
||||
use std::io::{Write};
|
||||
|
||||
@@ -7,19 +13,14 @@ use crate::utils::compression::{decompress_lzma, decompress_lz4};
|
||||
use crate::utils::lzop::{unlzop_to_file};
|
||||
use crate::utils::sparse::{unsparse_to_file};
|
||||
|
||||
//change whether the "userdata" partition is skipped
|
||||
// this is because the userdata partition is sometimes enourmous sizes like 27gb, and it is empty anyways
|
||||
// if you want to extract "userdata" you can disable the option
|
||||
static CONFIG_SKIP_USERDATA: bool = true;
|
||||
|
||||
pub fn is_mstar_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 32768).expect("Failed to read from file.");
|
||||
pub fn is_mstar_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 32768)?;
|
||||
let header_string = String::from_utf8_lossy(&header);
|
||||
|
||||
if header_string.contains("filepartload"){
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +32,8 @@ fn parse_number(s: &str) -> Option<u64> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_mstar(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let file = app_ctx.file;
|
||||
|
||||
let mut script = common::read_file(&file, 0, 32768)?;
|
||||
|
||||
@@ -132,12 +134,10 @@ pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box<dyn std
|
||||
|
||||
if partname == "unknown" {
|
||||
println!("- Unknown destination, skipping!");
|
||||
} else if partname == "userdata" && CONFIG_SKIP_USERDATA {
|
||||
println!("- Skipping userdata according to config!")
|
||||
} else {
|
||||
let data = common::read_file(&file, offset, size.try_into().unwrap())?;
|
||||
let out_data;
|
||||
let output_path = Path::new(&output_folder).join(format!("{}.bin", partname));
|
||||
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", partname));
|
||||
|
||||
if compression == "lzma" {
|
||||
println!("- Decompressing LZMA...");
|
||||
@@ -166,7 +166,7 @@ pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box<dyn std
|
||||
out_data = data;
|
||||
}
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&out_data)?;
|
||||
println!("-- Saved file!");
|
||||
|
||||
+15
-7
@@ -1,4 +1,9 @@
|
||||
use std::fs::File;
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "mtk_bdp", detect_func: is_mtk_bdp_file, run_func: extract_mtk_bdp }
|
||||
}
|
||||
|
||||
use std::path::{Path};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Seek, SeekFrom, Read, Write};
|
||||
@@ -72,7 +77,8 @@ fn find_bytes(data: &[u8], pattern: &[u8]) -> Option<usize> {
|
||||
data.windows(pattern.len()).position(|window| window == pattern)
|
||||
}
|
||||
|
||||
pub fn is_mtk_bdp_file(mut file: &File) -> Result<Option<MtkBdpContext>, Box<dyn std::error::Error>> {
|
||||
pub fn is_mtk_bdp_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let file_size = file.metadata()?.len();
|
||||
let mut data = Vec::new();
|
||||
|
||||
@@ -83,14 +89,16 @@ pub fn is_mtk_bdp_file(mut file: &File) -> Result<Option<MtkBdpContext>, Box<dyn
|
||||
file.read_to_end(&mut data)?;
|
||||
|
||||
if let Some(pos) = find_bytes(&data, &PITIT_MAGIC) {
|
||||
Ok(Some(MtkBdpContext {pitit_offset: start_offset + pos as u64}))
|
||||
Ok(Some(Box::new(MtkBdpContext {pitit_offset: start_offset + pos as u64})))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_mtk_bdp(mut file: &File, output_folder: &str, context: MtkBdpContext) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let offset = context.pitit_offset;
|
||||
pub fn extract_mtk_bdp(app_ctx: &ProgramContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let ctx = ctx.and_then(|c: Box<dyn Any>| c.downcast::<MtkBdpContext>().ok()).ok_or("Context is invalid or missing!")?;
|
||||
let offset = ctx.pitit_offset;
|
||||
println!("\nReading PITIT at: {}", offset);
|
||||
|
||||
file.seek(SeekFrom::Start(offset + 8))?;
|
||||
@@ -173,8 +181,8 @@ pub fn extract_mtk_bdp(mut file: &File, output_folder: &str, context: MtkBdpCont
|
||||
|
||||
let data = common::read_file(&file, bit_entry.offset as u64, bit_entry.size as usize)?;
|
||||
|
||||
let output_path = Path::new(&output_folder).join(format!("{}.bin", name));
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", name));
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().read(true).write(true).create(true).open(output_path)?;
|
||||
out_file.seek(SeekFrom::Start(bit_entry.offset_in_target_part as u64))?;
|
||||
out_file.write_all(&data)?;
|
||||
|
||||
+21
-12
@@ -1,5 +1,11 @@
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "mtk_pkg", detect_func: is_mtk_pkg_file, run_func: extract_mtk_pkg }
|
||||
}
|
||||
|
||||
use std::path::Path;
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write, Cursor, Seek, SeekFrom};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
|
||||
@@ -75,17 +81,17 @@ static HEADER_KEY: [u8; 16] = [
|
||||
|
||||
static HEADER_IV: [u8; 16] = [0x00; 16];
|
||||
|
||||
pub fn is_mtk_pkg_file(file: &File) -> Result<Option<MtkPkgContext>, Box<dyn std::error::Error>> {
|
||||
let mut encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?;
|
||||
pub fn is_mtk_pkg_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let mut encrypted_header = common::read_file(app_ctx.file, 0, HEADER_SIZE)?;
|
||||
let mut header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?;
|
||||
if &header[4..12] == MTK_HEADER_MAGIC {
|
||||
Ok(Some(MtkPkgContext { is_philips_variant: false, decrypted_header: header}))
|
||||
Ok(Some(Box::new(MtkPkgContext { is_philips_variant: false, decrypted_header: header})))
|
||||
} else {
|
||||
// try for philips which has additional 128 bytes at beginning
|
||||
encrypted_header = common::read_file(&file, PHILIPS_EXTRA_HEADER_SIZE as u64, HEADER_SIZE)?;
|
||||
encrypted_header = common::read_file(app_ctx.file, PHILIPS_EXTRA_HEADER_SIZE as u64, HEADER_SIZE)?;
|
||||
header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?;
|
||||
if &header[4..12] == MTK_HEADER_MAGIC {
|
||||
Ok(Some(MtkPkgContext { is_philips_variant: true, decrypted_header: header }))
|
||||
Ok(Some(Box::new(MtkPkgContext { is_philips_variant: true, decrypted_header: header })))
|
||||
} else {
|
||||
Ok(None)
|
||||
|
||||
@@ -93,16 +99,19 @@ pub fn is_mtk_pkg_file(file: &File) -> Result<Option<MtkPkgContext>, Box<dyn std
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_mtk_pkg(mut file: &File, output_folder: &str, context: MtkPkgContext) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_mtk_pkg(app_ctx: &ProgramContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let ctx = ctx.and_then(|c: Box<dyn Any>| c.downcast::<MtkPkgContext>().ok()).ok_or("Context is invalid or missing!")?;
|
||||
|
||||
let file_size = file.metadata()?.len();
|
||||
let header = context.decrypted_header;
|
||||
let header = ctx.decrypted_header;
|
||||
let mut hdr_reader = Cursor::new(header);
|
||||
let hdr: Header = hdr_reader.read_le()?;
|
||||
|
||||
println!("File info:\nFile size: {}\nVendor magic: {}\nVersion info: {}\nProduct name: {}" ,
|
||||
hdr.file_size, hdr.vendor_magic(), hdr.version(), hdr.product_name());
|
||||
|
||||
if context.is_philips_variant {
|
||||
if ctx.is_philips_variant {
|
||||
file.seek(SeekFrom::Start(HEADER_SIZE as u64 + PHILIPS_EXTRA_HEADER_SIZE as u64))?;
|
||||
} else {
|
||||
file.seek(SeekFrom::Start(HEADER_SIZE as u64))?;
|
||||
@@ -189,13 +198,13 @@ pub fn extract_mtk_pkg(mut file: &File, output_folder: &str, context: MtkPkgCont
|
||||
};
|
||||
|
||||
//for compressed part create temp file
|
||||
let output_path = Path::new(&output_folder).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"});
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let output_path = Path::new(app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"});
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().write(true).read(true)/* for lzhs */.create(true).open(&output_path)?;
|
||||
out_file.write_all(&out_data[CRYPTED_HEADER_SIZE + extra_header_len as usize..])?;
|
||||
|
||||
if part_entry.is_compressed() {
|
||||
let lzhs_out_path = Path::new(&output_folder).join(part_entry.name() + ".bin");
|
||||
let lzhs_out_path = Path::new(app_ctx.output_dir).join(part_entry.name() + ".bin");
|
||||
match decompress_lzhs_fs_file2file(&out_file, lzhs_out_path) {
|
||||
Ok(()) => {
|
||||
println!("- Decompressed Successfully!");
|
||||
|
||||
+21
-13
@@ -1,5 +1,11 @@
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "mtk_pkg_new", detect_func: is_mtk_pkg_new_file, run_func: extract_mtk_pkg_new }
|
||||
}
|
||||
|
||||
use std::path::Path;
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write, Cursor, Seek, SeekFrom};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
|
||||
@@ -64,34 +70,36 @@ impl PartEntry {
|
||||
|
||||
static HEADER_SIZE: usize = 0x170;
|
||||
|
||||
pub fn is_mtk_pkg_new_file(file: &File) -> Result<Option<MtkPkgNewContext>, Box<dyn std::error::Error>> {
|
||||
let encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?;
|
||||
pub fn is_mtk_pkg_new_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let encrypted_header = common::read_file(app_ctx.file, 0, HEADER_SIZE)?;
|
||||
for (key_hex, iv_hex, name) in keys::MTK_PKG_CUST {
|
||||
let key_array: [u8; 16] = hex::decode(key_hex)?.as_slice().try_into()?;
|
||||
let iv_array: [u8; 16] = hex::decode(iv_hex)?.as_slice().try_into()?;
|
||||
let try_decrypt = decrypt_aes128_cbc_nopad(&encrypted_header, &key_array, &iv_array)?;
|
||||
|
||||
if &try_decrypt[4..12] == MTK_HEADER_MAGIC {
|
||||
return Ok(Some(MtkPkgNewContext {
|
||||
return Ok(Some(Box::new(MtkPkgNewContext {
|
||||
matching_key_name: name.to_string(),
|
||||
matching_key_key: key_array,
|
||||
matching_key_iv: iv_array,
|
||||
decrypted_header: try_decrypt
|
||||
}));
|
||||
})));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn extract_mtk_pkg_new(mut file: &File, output_folder: &str, context: MtkPkgNewContext) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_mtk_pkg_new(app_ctx: &ProgramContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let ctx = ctx.and_then(|c: Box<dyn Any>| c.downcast::<MtkPkgNewContext>().ok()).ok_or("Context is invalid or missing!")?;
|
||||
let file_size = file.metadata()?.len();
|
||||
|
||||
//the key was founf, and header was decrypted at detection stage so we can reuse
|
||||
println!("Using key {}", context.matching_key_name);
|
||||
let key_array = context.matching_key_key;
|
||||
let iv_array = context.matching_key_iv;
|
||||
let header = context.decrypted_header;
|
||||
println!("Using key {}", ctx.matching_key_name);
|
||||
let key_array = ctx.matching_key_key;
|
||||
let iv_array = ctx.matching_key_iv;
|
||||
let header = ctx.decrypted_header;
|
||||
|
||||
let mut hdr_reader = Cursor::new(header);
|
||||
let hdr: Header = hdr_reader.read_le()?;
|
||||
@@ -147,13 +155,13 @@ pub fn extract_mtk_pkg_new(mut file: &File, output_folder: &str, context: MtkPkg
|
||||
};
|
||||
|
||||
//for compressed part create temp file
|
||||
let output_path = Path::new(&output_folder).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"});
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let output_path = Path::new(app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"});
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().write(true).read(true)/* for lzhs */.create(true).open(&output_path)?;
|
||||
out_file.write_all(&out_data[48 + extra_header_len as usize..])?;
|
||||
|
||||
if part_entry.is_compressed() {
|
||||
let lzhs_out_path = Path::new(&output_folder).join(part_entry.name() + ".bin");
|
||||
let lzhs_out_path = Path::new(app_ctx.output_dir).join(part_entry.name() + ".bin");
|
||||
match decompress_lzhs_fs_file2file(&out_file, lzhs_out_path) {
|
||||
Ok(()) => {
|
||||
println!("-- Decompressed Successfully!");
|
||||
|
||||
+21
-13
@@ -1,5 +1,11 @@
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "mtk_pkg_old", detect_func: is_mtk_pkg_old_file, run_func: extract_mtk_pkg_old }
|
||||
}
|
||||
|
||||
use std::path::Path;
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write, Cursor, Seek};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
|
||||
@@ -57,25 +63,27 @@ impl PartEntry {
|
||||
|
||||
static HEADER_SIZE: usize = 0x98;
|
||||
|
||||
pub fn is_mtk_pkg_old_file(mut file: &File) -> bool {
|
||||
let encrypted_header = common::read_file(&file, 0, HEADER_SIZE).expect("Failed to read from file.");
|
||||
pub fn is_mtk_pkg_old_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?;
|
||||
let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK));
|
||||
if &header[4..12] == MTK_HEADER_MAGIC {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else if &header[68..76] == MTK_HEADER_MAGIC {
|
||||
//check for 64 byte additional header used in some Sony and Philips firmwares and skip it
|
||||
file.seek(std::io::SeekFrom::Start(64)).expect("Failed to seek");
|
||||
true
|
||||
file.seek(std::io::SeekFrom::Start(64))?;
|
||||
Ok(Some(Box::new(())))
|
||||
} else if &header[132..140] == MTK_HEADER_MAGIC {
|
||||
//check for 128 byte additional header used in some Philips firmwares and skip it
|
||||
file.seek(std::io::SeekFrom::Start(128)).expect("Failed to seek");
|
||||
true
|
||||
file.seek(std::io::SeekFrom::Start(128))?;
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_mtk_pkg_old(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_mtk_pkg_old(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let file_size = file.metadata()?.len();
|
||||
let encrypted_header = common::read_exact(&mut file, HEADER_SIZE)?;
|
||||
let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK));
|
||||
@@ -118,13 +126,13 @@ pub fn extract_mtk_pkg_old(mut file: &File, output_folder: &str) -> Result<(), B
|
||||
};
|
||||
|
||||
//for compressed part create temp file
|
||||
let output_path = Path::new(&output_folder).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"});
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
let output_path = Path::new(app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"});
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().write(true).read(true)/* for lzhs */.create(true).open(&output_path)?;
|
||||
out_file.write_all(&out_data[extra_header_len as usize..])?;
|
||||
|
||||
if part_entry.is_compressed() {
|
||||
let lzhs_out_path = Path::new(&output_folder).join(part_entry.name() + ".bin");
|
||||
let lzhs_out_path = Path::new(app_ctx.output_dir).join(part_entry.name() + ".bin");
|
||||
match decompress_lzhs_fs_file2file_old(&out_file, lzhs_out_path) {
|
||||
Ok(()) => {
|
||||
println!("- Decompressed Successfully!");
|
||||
|
||||
+14
-8
@@ -1,4 +1,9 @@
|
||||
use std::fs::File;
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "novatek", detect_func: is_novatek_file, run_func: extract_novatek }
|
||||
}
|
||||
|
||||
use std::path::Path;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write};
|
||||
@@ -35,16 +40,17 @@ struct PartEntry {
|
||||
#[br(count = 16)] _md5_checksum: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn is_novatek_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 4).expect("Failed to read from file.");
|
||||
pub fn is_novatek_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 4)?;
|
||||
if header == b"NFWB" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_novatek(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_novatek(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let header: Header = file.read_le()?;
|
||||
|
||||
println!("File info:\nFirmware name: {}\nVersion: {}.{}\nData size: {}\nPart count: {}",
|
||||
@@ -63,9 +69,9 @@ pub fn extract_novatek(mut file: &File, output_folder: &str) -> Result<(), Box<d
|
||||
|
||||
let data = common::read_file(&file, entry.offset as u64, entry.size as usize)?;
|
||||
|
||||
let output_path = Path::new(&output_folder).join(format!("{}_{}.bin", e_i, entry.id));
|
||||
let output_path = Path::new(app_ctx.output_dir).join(format!("{}_{}.bin", e_i, entry.id));
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
|
||||
+15
-9
@@ -1,7 +1,12 @@
|
||||
use std::str;
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "nvt_timg", detect_func: is_nvt_timg_file, run_func: extract_nvt_timg }
|
||||
}
|
||||
|
||||
use std::path::{Path};
|
||||
use std::io::{Seek, Write};
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
|
||||
use crate::utils::common;
|
||||
@@ -44,16 +49,17 @@ impl PIMG {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_nvt_timg_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 4).expect("Failed to read from file.");
|
||||
pub fn is_nvt_timg_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 4)?;
|
||||
if header == b"TIMG" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_nvt_timg(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_nvt_timg(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let file_size = file.metadata()?.len();
|
||||
let timg: TIMG = file.read_le()?;
|
||||
println!("File info:\nData size: {}", timg.data_size);
|
||||
@@ -72,7 +78,7 @@ pub fn extract_nvt_timg(mut file: &File, output_folder: &str) -> Result<(), Box<
|
||||
println!("\n#{} - {}, Size: {}, Dest: {}, Compression: {}", pimg_i, pimg.name(), pimg.size, pimg.dest_dev(), pimg.comp_type());
|
||||
|
||||
let out_data;
|
||||
let output_path = Path::new(&output_folder).join(pimg.name() + ".bin");
|
||||
let output_path = Path::new(app_ctx.output_dir).join(pimg.name() + ".bin");
|
||||
|
||||
if pimg.comp_type() == "gzip" && data.starts_with(b"\x1F\x8B") { //additionally check for gzip header, because sometimes its deceptive
|
||||
println!("- Decompressing gzip...");
|
||||
@@ -89,7 +95,7 @@ pub fn extract_nvt_timg(mut file: &File, output_folder: &str) -> Result<(), Box<
|
||||
out_data = data;
|
||||
}
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
|
||||
+20
-12
@@ -1,4 +1,9 @@
|
||||
use std::fs::File;
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "pana_dvd", detect_func: is_pana_dvd_file, run_func: extract_pana_dvd }
|
||||
}
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write, Read, Cursor, Seek, SeekFrom};
|
||||
@@ -144,38 +149,41 @@ pub fn find_aes_key_pair<'a>(key_array: &'a [(&'a str, &'a str, &'a str)], data:
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn is_pana_dvd_file(file: &File) -> Result<Option<PanaDvdContext>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(&file, 0, 64)?;
|
||||
pub fn is_pana_dvd_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 64)?;
|
||||
if let Some(matching_key) = find_key(&keys::PANA_DVD_KEYONLY, &header, b"PROG", 0)? {
|
||||
Ok(Some(PanaDvdContext {
|
||||
Ok(Some(Box::new(PanaDvdContext {
|
||||
matching_key: matching_key,
|
||||
base_hdr_size: 0,
|
||||
is_aes: false,
|
||||
aes_key: None,
|
||||
aes_iv: None,
|
||||
}))
|
||||
})))
|
||||
} else if header.starts_with(b"PANASONIC\x00\x00\x00") && let Some(matching_key) = find_key(&keys::PANA_DVD_KEYONLY, &header, b"PROG", 48)? {
|
||||
Ok(Some(PanaDvdContext {
|
||||
Ok(Some(Box::new(PanaDvdContext {
|
||||
matching_key: matching_key,
|
||||
base_hdr_size: 48,
|
||||
is_aes: false,
|
||||
aes_key: None,
|
||||
aes_iv: None,
|
||||
}))
|
||||
})))
|
||||
} else if let Some((aes_key, aes_iv, matching_key)) = find_aes_key_pair(&keys::PANA_DVD_AESPAIR, &header, b"PANASONIC", 32)? {
|
||||
Ok(Some(PanaDvdContext {
|
||||
Ok(Some(Box::new(PanaDvdContext {
|
||||
matching_key: matching_key,
|
||||
base_hdr_size: 48,
|
||||
is_aes: true,
|
||||
aes_key: Some(aes_key),
|
||||
aes_iv: Some(aes_iv),
|
||||
}))
|
||||
})))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_pana_dvd(mut file: &File, output_folder: &str, context: PanaDvdContext) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_pana_dvd(app_ctx: &ProgramContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let context = ctx.and_then(|c: Box<dyn Any>| c.downcast::<PanaDvdContext>().ok()).ok_or("Context is invalid or missing!")?;
|
||||
|
||||
let mut data = Vec::new(); // we need to load the entire file into memory so we can swap it with AES decrypted data if its AES encrypted
|
||||
file.read_to_end(&mut data)?;
|
||||
let mut file_reader = Cursor::new(data);
|
||||
@@ -209,12 +217,12 @@ pub fn extract_pana_dvd(mut file: &File, output_folder: &str, context: PanaDvdCo
|
||||
if file_entries.len() == 1 {
|
||||
//only one file, standard extraction
|
||||
println!("File contains no extra sub-files...\n");
|
||||
extract_file(&mut file_reader, file_entries[0].offset as u64, file_entries[0].base_offset as u64, matching_key, output_folder)?;
|
||||
extract_file(&mut file_reader, file_entries[0].offset as u64, file_entries[0].base_offset as u64, matching_key, app_ctx.output_dir)?;
|
||||
} else {
|
||||
println!("File contains {} sub-files...", file_entries.len());
|
||||
for (i, file_entry ) in file_entries.iter().enumerate() {
|
||||
println!("\nExtracting file {}/{} - Offset: {}, base: {}", i + 1, file_entries.len(), file_entry.offset, file_entry.base_offset);
|
||||
extract_file(&mut file_reader, file_entry.offset as u64, file_entry.base_offset as u64, matching_key, &format!("{}/file_{}", output_folder, i + 1))?;
|
||||
extract_file(&mut file_reader, file_entry.offset as u64, file_entry.base_offset as u64, matching_key, &format!("{}/file_{}", app_ctx.output_dir, i + 1))?;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-9
@@ -1,4 +1,9 @@
|
||||
use std::fs::{File};
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "pfl_upg", detect_func: is_pfl_upg_file, run_func: extract_pfl_upg }
|
||||
}
|
||||
|
||||
use rsa::{RsaPublicKey, BigUint};
|
||||
use hex::decode;
|
||||
use std::path::Path;
|
||||
@@ -43,12 +48,12 @@ impl FileHeader {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_pfl_upg_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 8).expect("Failed to read from file.");
|
||||
pub fn is_pfl_upg_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 8)?;
|
||||
if header == b"2SWU3TXV" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +89,8 @@ fn decrypt_aes256_ecb(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, Box<dyn
|
||||
Ok(buffer)
|
||||
}
|
||||
|
||||
pub fn extract_pfl_upg(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_pfl_upg(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let header: Header = file.read_le()?;
|
||||
let signature = common::read_exact(&mut file, 128)?;
|
||||
let _ = common::read_exact(&mut file, 32)?; //unknown
|
||||
@@ -157,7 +163,7 @@ pub fn extract_pfl_upg(mut file: &File, output_folder: &str) -> Result<(), Box<d
|
||||
//its a folder not a file
|
||||
if (file_header.attributes[3] & (1 << 1)) != 0 {
|
||||
println!("\nFolder - {}", file_header.file_name());
|
||||
let output_path = Path::new(&output_folder).join(file_header.file_name().trim_start_matches('/'));
|
||||
let output_path = Path::new(app_ctx.output_dir).join(file_header.file_name().trim_start_matches('/'));
|
||||
fs::create_dir_all(output_path)?;
|
||||
continue
|
||||
}
|
||||
@@ -175,7 +181,7 @@ pub fn extract_pfl_upg(mut file: &File, output_folder: &str) -> Result<(), Box<d
|
||||
println!("\nFile - {}, Size: {}", file_name, file_header.real_size);
|
||||
let data = common::read_exact(&mut data_reader, file_header.stored_size as usize)?;
|
||||
|
||||
let output_path = Path::new(&output_folder).join(file_name.trim_start_matches('/'));
|
||||
let output_path = Path::new(app_ctx.output_dir).join(file_name.trim_start_matches('/'));
|
||||
let output_path_parent = output_path.parent().expect("Failed to get parent of the output path!");
|
||||
|
||||
//prevent collisions
|
||||
@@ -188,7 +194,7 @@ pub fn extract_pfl_upg(mut file: &File, output_folder: &str) -> Result<(), Box<d
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
|
||||
+17
-10
@@ -1,5 +1,11 @@
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "pup", detect_func: is_pup_file, run_func: extract_pup }
|
||||
}
|
||||
|
||||
use std::path::{Path};
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
use std::io::{Write, Seek, SeekFrom};
|
||||
|
||||
@@ -50,16 +56,17 @@ struct BlockEntry {
|
||||
size: u32,
|
||||
}
|
||||
|
||||
pub fn is_pup_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 4).expect("Failed to read from file.");
|
||||
pub fn is_pup_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 4)?;
|
||||
if header == b"\x4F\x15\x3D\x1D" || header == b"\x54\x14\xF5\xEE" { //ps4, ps5
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_pup(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_pup(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let header: Header = file.read_le()?;
|
||||
|
||||
println!("File info:\nFile size: {}\nEntry count: {}",
|
||||
@@ -132,9 +139,9 @@ pub fn extract_pup(mut file: &File, output_folder: &str) -> Result<(), Box<dyn s
|
||||
out_data = data;
|
||||
}
|
||||
|
||||
let output_path = Path::new(&output_folder).join(format!("{}.bin", entry.id()));
|
||||
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", entry.id()));
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new()
|
||||
.append(true)
|
||||
.create(true)
|
||||
@@ -158,9 +165,9 @@ pub fn extract_pup(mut file: &File, output_folder: &str) -> Result<(), Box<dyn s
|
||||
out_data = data;
|
||||
}
|
||||
|
||||
let output_path = Path::new(&output_folder).join(format!("{}.bin", entry.id()));
|
||||
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", entry.id()));
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
|
||||
+17
-10
@@ -1,4 +1,10 @@
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "roku", detect_func: is_roku_file, run_func: extract_roku }
|
||||
}
|
||||
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::path::Path;
|
||||
use std::io::{Write, Seek, Read, Cursor};
|
||||
use tar::Archive;
|
||||
@@ -55,18 +61,19 @@ impl ImageHeader {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_roku_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 32).expect("Failed to read from file.");
|
||||
let try_decrypt_header = decrypt_aes128_cbc_nopad(&header, &FILE_KEY, &FILE_IV).expect("Decryption error!");
|
||||
pub fn is_roku_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 32)?;
|
||||
let try_decrypt_header = decrypt_aes128_cbc_nopad(&header, &FILE_KEY, &FILE_IV)?;
|
||||
|
||||
if try_decrypt_header.starts_with(b"manifest\x00\x00\x00\x00\x00\x00\x00\x00") {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_roku(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_roku(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let mut encrypted_data = Vec::new();
|
||||
file.read_to_end(&mut encrypted_data)?;
|
||||
|
||||
@@ -109,7 +116,7 @@ pub fn extract_roku(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
common::read_exact(&mut image_reader, image.size1 as usize - image.data_start_offset as usize)?
|
||||
};
|
||||
|
||||
let folder_path = Path::new(&output_folder).join(&path);
|
||||
let folder_path = Path::new(app_ctx.output_dir).join(&path);
|
||||
let output_path = Path::new(&folder_path).join(format!("{}_{}.bin", i, image.type_string()));
|
||||
|
||||
fs::create_dir_all(&folder_path)?;
|
||||
@@ -123,9 +130,9 @@ pub fn extract_roku(mut file: &File, output_folder: &str) -> Result<(), Box<dyn
|
||||
|
||||
} else {
|
||||
println!("\nOther/Unknown file: {:?}", path);
|
||||
let output_path = Path::new(&output_folder).join(&path);
|
||||
let output_path = Path::new(app_ctx.output_dir).join(&path);
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&contents)?;
|
||||
|
||||
|
||||
+16
-9
@@ -1,3 +1,9 @@
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "ruf", detect_func: is_ruf_file, run_func: extract_ruf }
|
||||
}
|
||||
|
||||
use std::path::{Path};
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
@@ -69,24 +75,25 @@ impl RufEntry {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ruf_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 3).expect("Failed to read from file.");
|
||||
if header == b"RUF" {
|
||||
true
|
||||
pub fn is_ruf_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 6)?;
|
||||
if header == b"RUF\x00\x00\x00" {
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_ruf(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_ruf(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let header: RufHeader = file.read_be()?;
|
||||
if header.is_dual_ruf() {
|
||||
println!("\nDual RUF detected! Extracting 1st RUF...\n");
|
||||
actually_extract_ruf(&file, &format!("{}/RUF_1", output_folder), 0)?;
|
||||
actually_extract_ruf(file, &format!("{}/RUF_1", app_ctx.output_dir), 0)?;
|
||||
println!("\nExtracting 2nd RUF...\n");
|
||||
actually_extract_ruf(&file, &format!("{}/RUF_2", output_folder), 41943088)?;
|
||||
actually_extract_ruf(file, &format!("{}/RUF_2", app_ctx.output_dir), 41943088)?;
|
||||
} else {
|
||||
actually_extract_ruf(&file, &output_folder, 0)?;
|
||||
actually_extract_ruf(file, app_ctx.output_dir, 0)?;
|
||||
}
|
||||
|
||||
println!("\nExtraction finished!");
|
||||
|
||||
+20
-12
@@ -1,4 +1,9 @@
|
||||
use std::fs::File;
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "rvp", detect_func: is_rvp_file, run_func: extract_rvp }
|
||||
}
|
||||
|
||||
use std::path::Path;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write, Read, Cursor, Seek};
|
||||
@@ -13,26 +18,29 @@ fn decrypt_xor(data: &[u8]) -> Vec<u8> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn is_rvp_file(mut file: &File) -> bool {
|
||||
pub fn is_rvp_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
//MVP
|
||||
let header = common::read_file(&file, 0, 4).expect("Failed to read from file.");
|
||||
let header = common::read_file(file, 0, 4)?;
|
||||
if header == b"UPDT" {
|
||||
file.seek(std::io::SeekFrom::Start(36)).expect("Failed to seek"); //skip rest of header
|
||||
return true;
|
||||
file.seek(std::io::SeekFrom::Start(36))?; //skip rest of header // NOT GOOD PRACTICE SHOULD BE REMOVED
|
||||
return Ok(Some(Box::new(())))
|
||||
}
|
||||
|
||||
//RVP
|
||||
let bytes = common::read_file(&file, 16, 18).expect("Failed to read from file.");
|
||||
let bytes = common::read_file(file, 16, 18)?;
|
||||
for (_i, &b) in bytes.iter().enumerate().step_by(2) {
|
||||
if b != 0xA3 {
|
||||
return false;
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
file.seek(std::io::SeekFrom::Start(64)).expect("Failed to seek"); //skip rest of header
|
||||
true
|
||||
|
||||
file.seek(std::io::SeekFrom::Start(64))?; //skip rest of header // NOT GOOD PRACTICE SHOULD BE REMOVED
|
||||
Ok(Some(Box::new(())))
|
||||
}
|
||||
|
||||
pub fn extract_rvp(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_rvp(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let mut obf_data = Vec::new(); //we sadly cannot deXOR on the fly because of its 32 byte pattern
|
||||
file.read_to_end(&mut obf_data)?;
|
||||
println!("DeXORing data..");
|
||||
@@ -114,9 +122,9 @@ pub fn extract_rvp(mut file: &File, output_folder: &str) -> Result<(), Box<dyn s
|
||||
|
||||
println!("Size: {}", size);
|
||||
let data = common::read_exact(&mut data_reader, size as usize)?;
|
||||
let output_path = Path::new(&output_folder).join(if name=="" {format!("{}.bin", i)} else {format!("{}_{}.bin", i, name)});
|
||||
let output_path = Path::new(app_ctx.output_dir).join(if name=="" {format!("{}.bin", i)} else {format!("{}_{}.bin", i, name)});
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&data)?;
|
||||
|
||||
|
||||
+16
-10
@@ -1,7 +1,12 @@
|
||||
//sddl_dec 5.0
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "sddl_sec", detect_func: is_sddl_sec_file, run_func: extract_sddl_sec }
|
||||
}
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Cursor, Seek, SeekFrom, Write};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
|
||||
@@ -98,13 +103,13 @@ static DEC_IV: [u8; 16] = [
|
||||
0xCD, 0x88, 0x38, 0xC4, 0xB9, 0x0C, 0x76, 0x66,
|
||||
];
|
||||
|
||||
pub fn is_sddl_sec_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 32).expect("Failed to read from file.");
|
||||
pub fn is_sddl_sec_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 32)?;
|
||||
let deciph_header = decipher(&header);
|
||||
if deciph_header.starts_with(b"\x11\x22\x33\x44") {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +155,8 @@ fn decipher(s: &[u8]) -> Vec<u8> {
|
||||
out
|
||||
}
|
||||
|
||||
pub fn extract_sddl_sec(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_sddl_sec(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let mut hdr_reader = Cursor::new(decipher(&common::read_exact(&mut file, 32)?));
|
||||
let hdr: SddlSecHeader = hdr_reader.read_be()?;
|
||||
|
||||
@@ -168,10 +174,10 @@ pub fn extract_sddl_sec(mut file: &File, output_folder: &str) -> Result<(), Box<
|
||||
let data = common::read_exact(&mut file, entry_header.size() as usize)?;
|
||||
let dec_data = decrypt_aes128_cbc_pcks7(&data, &DEC_KEY, &DEC_IV)?;
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
//detect the file type based on the counts of each file
|
||||
if i == 0 { //SDIT.FDI file
|
||||
let output_path = Path::new(&output_folder).join(entry_header.name());
|
||||
let output_path = Path::new(app_ctx.output_dir).join(entry_header.name());
|
||||
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&dec_data)?;
|
||||
println!("-- Saved file!");
|
||||
@@ -210,11 +216,11 @@ pub fn extract_sddl_sec(mut file: &File, output_folder: &str) -> Result<(), Box<
|
||||
let file_name = common::string_from_bytes(&file_name_bytes);
|
||||
println!("--- File name: {}", file_name);
|
||||
|
||||
let out_folder_path = Path::new(&output_folder).join(source_name);
|
||||
let out_folder_path = Path::new(app_ctx.output_dir).join(source_name);
|
||||
fs::create_dir_all(&out_folder_path)?;
|
||||
output_path = Path::new(&out_folder_path).join(file_name);
|
||||
} else {
|
||||
output_path = Path::new(&output_folder).join(format!("{}.bin", source_name));
|
||||
output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", source_name));
|
||||
}
|
||||
|
||||
let data = common::read_exact(&mut content_reader, content_header.size as usize)?;
|
||||
|
||||
+15
-8
@@ -1,5 +1,11 @@
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "slp", detect_func: is_slp_file, run_func: extract_slp }
|
||||
}
|
||||
|
||||
use std::path::{Path};
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Write, Seek, SeekFrom};
|
||||
use binrw::{BinRead, BinReaderExt};
|
||||
|
||||
@@ -46,16 +52,17 @@ struct EntryNew {
|
||||
#[br(count = 12)] _unk2: Vec<u8>,
|
||||
}
|
||||
|
||||
pub fn is_slp_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 4).expect("Failed to read from file.");
|
||||
pub fn is_slp_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 4)?;
|
||||
if header == b"SLP\x00" {
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_slp(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_slp(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let header: Header = file.read_le()?;
|
||||
|
||||
println!("File info:\nModel: {}\nVersion: {}\nFirmware: {}\nNew type: {}\n",
|
||||
@@ -92,9 +99,9 @@ pub fn extract_slp(mut file: &File, output_folder: &str) -> Result<(), Box<dyn s
|
||||
file.seek(SeekFrom::Start(entry.offset.into()))?;
|
||||
let data = common::read_exact(&mut file, entry.size as usize)?;
|
||||
|
||||
let output_path = Path::new(&output_folder).join(format!("{}.bin", i));
|
||||
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", i));
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create(true)
|
||||
|
||||
+21
-10
@@ -1,3 +1,9 @@
|
||||
use std::any::Any;
|
||||
use crate::{ProgramContext, formats::Format};
|
||||
pub fn format() -> Format {
|
||||
Format { name: "sony_bdp", detect_func: is_sony_bdp_file, run_func: extract_sony_bdp }
|
||||
}
|
||||
|
||||
use std::fs::File;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fs::{self, OpenOptions};
|
||||
@@ -57,16 +63,17 @@ struct Entry {
|
||||
size: u32,
|
||||
}
|
||||
|
||||
pub fn is_sony_bdp_file(file: &File) -> bool {
|
||||
let header = common::read_file(&file, 0, 4).expect("Failed to read from file.");
|
||||
pub fn is_sony_bdp_file(app_ctx: &ProgramContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let header = common::read_file(app_ctx.file, 0, 4)?;
|
||||
if header == b"\x01\x73\xEC\xC9" || header == b"\x01\x73\xEC\x1F" || header == b"\xEC\x7D\xB0\xB0" { //MSB1x, MSB0x, BDPPxx
|
||||
true
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
false
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_sony_bdp(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_sony_bdp(app_ctx: &ProgramContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file;
|
||||
let obf_header = common::read_exact(&mut file, 300)?;
|
||||
let header = hex_substitute(&obf_header);
|
||||
let mut hdr_reader = Cursor::new(header);
|
||||
@@ -96,10 +103,10 @@ pub fn extract_sony_bdp(mut file: &File, output_folder: &str) -> Result<(), Box<
|
||||
let obf_data = common::read_file(&file, entry.offset as u64, entry.size as usize)?;
|
||||
let data = hex_substitute(&obf_data);
|
||||
|
||||
let output_path = Path::new(&output_folder).join(format!("{}.bin", i));
|
||||
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", i));
|
||||
last_file_path = Some(output_path.clone());
|
||||
|
||||
fs::create_dir_all(&output_folder)?;
|
||||
fs::create_dir_all(app_ctx.output_dir)?;
|
||||
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
|
||||
out_file.write_all(&data)?;
|
||||
|
||||
@@ -110,11 +117,15 @@ pub fn extract_sony_bdp(mut file: &File, output_folder: &str) -> Result<(), Box<
|
||||
//The last file is often a Mtk BDP file so we can extract that here.
|
||||
if last_file_path.is_some() {
|
||||
println!("\nChecking if it's also MTK BDP...");
|
||||
|
||||
let last_file = File::open(last_file_path.unwrap())?;
|
||||
if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&last_file)? {
|
||||
let mtk_extraction_path = format!("{}/{}", app_ctx.output_dir, i - 1);
|
||||
let ctx: ProgramContext = ProgramContext { file: &last_file, output_dir: &mtk_extraction_path };
|
||||
|
||||
if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&ctx)? {
|
||||
println!("- MTK BDP file detected!\n");
|
||||
let mtk_extraction_path = format!("{}/{}", &output_folder, i - 1);
|
||||
formats::mtk_bdp::extract_mtk_bdp(&last_file, &mtk_extraction_path, result)?;
|
||||
|
||||
formats::mtk_bdp::extract_mtk_bdp(&ctx, Some(result))?;
|
||||
} else {
|
||||
println!("- Not an MTK BDP file.");
|
||||
}
|
||||
|
||||
+17
-122
@@ -6,6 +6,7 @@ use clap::Parser;
|
||||
use std::path::{PathBuf};
|
||||
use std::io::{self};
|
||||
use std::fs::{self, File};
|
||||
use crate::formats::{Format, get_registry};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Args {
|
||||
@@ -13,6 +14,11 @@ struct Args {
|
||||
output_folder: Option<String>,
|
||||
}
|
||||
|
||||
pub struct ProgramContext<'a> {
|
||||
pub file: &'a std::fs::File,
|
||||
pub output_dir: &'a str,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("unixtract Firmware extractor");
|
||||
let args = Args::parse();
|
||||
@@ -39,130 +45,19 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if path.is_dir() {
|
||||
if formats::samsung_old::is_samsung_old_dir(&path) {
|
||||
println!("Samsung old firmware dir detected!\n");
|
||||
formats::samsung_old::extract_samsung_old(&path, &output_path)?
|
||||
} else {
|
||||
println!("Input format not recognized!");
|
||||
}
|
||||
} else {
|
||||
let file = File::open(path)?;
|
||||
if formats::sddl_sec::is_sddl_sec_file(&file) {
|
||||
println!("SDDL.SEC file detected!");
|
||||
formats::sddl_sec::extract_sddl_sec(&file, &output_path)?;
|
||||
}
|
||||
else if formats::invincible_image::is_invincible_image_file(&file) {
|
||||
println!("INVINCIBLE_IMAGE file detected!");
|
||||
formats::invincible_image::extract_invincible_image(&file, &output_path)?;
|
||||
}
|
||||
else if formats::msd10::is_msd10_file(&file) {
|
||||
println!("MSD10 file detected!");
|
||||
formats::msd10::extract_msd10(&file, &output_path)?;
|
||||
}
|
||||
else if formats::msd11::is_msd11_file(&file) {
|
||||
println!("MSD11 file detected!");
|
||||
formats::msd11::extract_msd11(&file, &output_path)?;
|
||||
}
|
||||
else if formats::nvt_timg::is_nvt_timg_file(&file) {
|
||||
println!("Novatek TIMG file detected!");
|
||||
formats::nvt_timg::extract_nvt_timg(&file, &output_path)?;
|
||||
}
|
||||
else if formats::bdl::is_bdl_file(&file) {
|
||||
println!("BDL file detected!");
|
||||
formats::bdl::extract_bdl(&file, &output_path)?;
|
||||
}
|
||||
else if formats::android_ota_payload::is_android_ota_payload_file(&file) {
|
||||
println!("Android OTA payload file detected!");
|
||||
formats::android_ota_payload::extract_android_ota_payload(&file, &output_path)?;
|
||||
}
|
||||
else if formats::novatek::is_novatek_file(&file) {
|
||||
println!("Novatek file detected!");
|
||||
formats::novatek::extract_novatek(&file, &output_path)?;
|
||||
}
|
||||
else if formats::slp::is_slp_file(&file) {
|
||||
println!("SLP file detected!");
|
||||
formats::slp::extract_slp(&file, &output_path)?;
|
||||
}
|
||||
else if formats::epk1::is_epk1_file(&file) {
|
||||
println!("EPK1 file detected!");
|
||||
formats::epk1::extract_epk1(&file, &output_path)?;
|
||||
}
|
||||
else if formats::epk2b::is_epk2b_file(&file) {
|
||||
println!("EPK2B file detected!");
|
||||
formats::epk2b::extract_epk2b(&file, &output_path)?;
|
||||
}
|
||||
//epk with encrypted header - it can be epk2 or epk3 so we need to check
|
||||
else if formats::epk::is_epk_file(&file) {
|
||||
println!("EPK file detected!");
|
||||
formats::epk::extract_epk(&file, &output_path)?;
|
||||
}
|
||||
//epk2 with unencrypted header
|
||||
else if formats::epk2::is_epk2_file(&file) {
|
||||
println!("EPK2 file detected!");
|
||||
formats::epk2::extract_epk2(&file, &output_path)?;
|
||||
}
|
||||
else if formats::ruf::is_ruf_file(&file) {
|
||||
println!("RUF file detected!");
|
||||
formats::ruf::extract_ruf(&file, &output_path)?;
|
||||
}
|
||||
else if formats::funai_upg::is_funai_upg_file(&file) {
|
||||
println!("Funai UPG file detected!");
|
||||
formats::funai_upg::extract_funai_upg(&file, &output_path)?;
|
||||
}
|
||||
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 let Some(result) = formats::pana_dvd::is_pana_dvd_file(&file)? {
|
||||
println!("PANA_DVD file detected!");
|
||||
formats::pana_dvd::extract_pana_dvd(&file, &output_path, result)?;
|
||||
}
|
||||
else if formats::pup::is_pup_file(&file) {
|
||||
println!("PUP file detected!");
|
||||
formats::pup::extract_pup(&file, &output_path)?;
|
||||
}
|
||||
else if formats::sony_bdp::is_sony_bdp_file(&file) {
|
||||
println!("Sony BDP file detected!");
|
||||
formats::sony_bdp::extract_sony_bdp(&file, &output_path)?;
|
||||
}
|
||||
else if formats::rvp::is_rvp_file(&file) {
|
||||
println!("RVP/MVP file detected!");
|
||||
formats::rvp::extract_rvp(&file, &output_path)?;
|
||||
}
|
||||
else if formats::mstar::is_mstar_file(&file) {
|
||||
println!("Mstar upgrade file detected!");
|
||||
formats::mstar::extract_mstar(&file, &output_path)?;
|
||||
}
|
||||
else if formats::roku::is_roku_file(&file) {
|
||||
println!("Roku file detected!");
|
||||
formats::roku::extract_roku(&file, &output_path)?;
|
||||
}
|
||||
else if let Some(result) = formats::mtk_pkg::is_mtk_pkg_file(&file)? {
|
||||
println!("MTK PKG file detected!");
|
||||
formats::mtk_pkg::extract_mtk_pkg(&file, &output_path, result)?;
|
||||
}
|
||||
else if formats::mtk_pkg_old::is_mtk_pkg_old_file(&file) {
|
||||
println!("MTK PKG (Old) file detected!");
|
||||
formats::mtk_pkg_old::extract_mtk_pkg_old(&file, &output_path)?;
|
||||
}
|
||||
else if let Some(result) = formats::mtk_pkg_new::is_mtk_pkg_new_file(&file)? {
|
||||
println!("MTK PKG (New) file detected!");
|
||||
formats::mtk_pkg_new::extract_mtk_pkg_new(&file, &output_path, result)?;
|
||||
}
|
||||
else if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&file)? {
|
||||
println!("MTK BDP file detected!");
|
||||
formats::mtk_bdp::extract_mtk_bdp(&file, &output_path, result)?;
|
||||
}
|
||||
else {
|
||||
println!("Input format not recognized!");
|
||||
|
||||
let file = File::open(path)?;
|
||||
let program_context: ProgramContext = ProgramContext { file: &file, output_dir: &output_path };
|
||||
let formats: Vec<Format> = get_registry();
|
||||
|
||||
for format in formats {
|
||||
if let Some(ctx) = (format.detect_func)(&program_context)? {
|
||||
println!("{} detected!", format.name);
|
||||
(format.run_func)(&program_context, Some(ctx))?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
println!("\nInput format not recognized!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user