bring back samsung_old - by adding InputTarget system

This commit is contained in:
theubusu
2026-02-06 15:08:38 +01:00
parent 43128ca117
commit 9b059c67ab
32 changed files with 291 additions and 224 deletions
+2 -1
View File
@@ -8,7 +8,7 @@ pub struct Format {
} }
pub mod mstar; pub mod mstar;
//pub mod samsung_old; not sure what to do with this pub mod samsung_old;
pub mod nvt_timg; pub mod nvt_timg;
pub mod pfl_upg; pub mod pfl_upg;
pub mod sddl_sec; pub mod sddl_sec;
@@ -45,6 +45,7 @@ pub mod mtk_bdp;
pub fn get_registry() -> Vec<Format> { pub fn get_registry() -> Vec<Format> {
return vec![ return vec![
crate::formats::mstar::format(), crate::formats::mstar::format(),
crate::formats::samsung_old::format(),
crate::formats::nvt_timg::format(), crate::formats::nvt_timg::format(),
crate::formats::pfl_upg::format(), crate::formats::pfl_upg::format(),
crate::formats::sddl_sec::format(), crate::formats::sddl_sec::format(),
+8 -5
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "amlogic", detector_func: is_amlogic_file, extractor_func: extract_amlogic } Format { name: "amlogic", detector_func: is_amlogic_file, extractor_func: extract_amlogic }
} }
@@ -50,7 +50,9 @@ impl ItemEntry {
} }
pub fn is_amlogic_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_amlogic_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 8, 4)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 8, 4)?;
if header == b"\x56\x19\xB5\x27" { if header == b"\x56\x19\xB5\x27" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -59,7 +61,8 @@ pub fn is_amlogic_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box
} }
pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
file.seek(SeekFrom::Start(0))?; file.seek(SeekFrom::Start(0))?;
let header: ImageHeader = file.read_le()?; let header: ImageHeader = file.read_le()?;
@@ -91,8 +94,8 @@ pub fn extract_amlogic(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Resu
let data = common::read_file(&file, item.offset_in_image, item.item_size as usize)?; 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 extension = if item.item_type() == "PARTITION" {"img"} else {&item.item_type()};
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.{}", item.name(), extension)); let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.{}", item.name(), extension));
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
if item.is_sparse() { if item.is_sparse() {
println!("- Unsparsing..."); println!("- Unsparsing...");
+8 -5
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "android_ota_payload", detector_func: is_android_ota_payload_file, extractor_func: extract_android_ota_payload } Format { name: "android_ota_payload", detector_func: is_android_ota_payload_file, extractor_func: extract_android_ota_payload }
} }
@@ -23,7 +23,9 @@ struct Header {
} }
pub fn is_android_ota_payload_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_android_ota_payload_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 4)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 4)?;
if header == b"CrAU" { if header == b"CrAU" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -32,7 +34,8 @@ pub fn is_android_ota_payload_file(app_ctx: &AppContext) -> Result<Option<Box<dy
} }
pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let header: Header = file.read_be()?; let header: Header = file.read_be()?;
println!("File info:\nFormat version: {}\nManifest size: {}", header.file_format_version, header.manifest_size); println!("File info:\nFormat version: {}\nManifest size: {}", header.file_format_version, header.manifest_size);
@@ -82,8 +85,8 @@ pub fn extract_android_ota_payload(app_ctx: &AppContext, _ctx: Option<Box<dyn An
break break
} }
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", partition.partition_name)); 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)?; let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?; out_file.write_all(&out_data)?;
} }
+7 -4
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "bdl", detector_func: is_bdl_file, extractor_func: extract_bdl } Format { name: "bdl", detector_func: is_bdl_file, extractor_func: extract_bdl }
} }
@@ -86,7 +86,9 @@ impl PkgEntry {
} }
pub fn is_bdl_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_bdl_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 4)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 4)?;
if header == b"ibdl" { if header == b"ibdl" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -95,7 +97,8 @@ pub fn is_bdl_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn
} }
pub fn extract_bdl(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_bdl(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let header: BdlHeader = file.read_le()?; let header: BdlHeader = file.read_le()?;
println!("File info:\nPackage count: {}\nDate: {}\nManufacturer: {}\nModel: {}\nVersion: {}\nInfo: {}", println!("File info:\nPackage count: {}\nDate: {}\nManufacturer: {}\nModel: {}\nVersion: {}\nInfo: {}",
@@ -122,7 +125,7 @@ pub fn extract_bdl(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(
pkg_entries.push(pkg_entry); pkg_entries.push(pkg_entry);
} }
let pkg_folder = Path::new(app_ctx.output_dir).join(pkg_header.name()); let pkg_folder = Path::new(&app_ctx.output_dir).join(pkg_header.name());
fs::create_dir_all(&pkg_folder)?; fs::create_dir_all(&pkg_folder)?;
for (i, pkg_entry) in pkg_entries.iter().enumerate() { for (i, pkg_entry) in pkg_entries.iter().enumerate() {
+5 -3
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "epk", detector_func: is_epk_file, extractor_func: extract_epk } Format { name: "epk", detector_func: is_epk_file, extractor_func: extract_epk }
} }
@@ -12,8 +12,9 @@ pub struct EpkContext {
} }
pub fn is_epk_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_epk_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let versions = common::read_file(app_ctx.file, 1712, 36)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let versions = common::read_file(&file, 1712, 36)?;
if let Some(epk_version) = check_epk_version(&versions) { if let Some(epk_version) = check_epk_version(&versions) {
Ok(Some(Box::new(EpkContext {epk_version}))) Ok(Some(Box::new(EpkContext {epk_version})))
} else { } else {
@@ -51,9 +52,10 @@ fn match_with_pattern(data: &[u8], pattern: &str) -> bool {
} }
pub fn extract_epk(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_epk(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let ctx = ctx.and_then(|c| c.downcast::<EpkContext>().ok()).ok_or("Context is invalid or missing!")?; 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 versions = common::read_file(&file, 1712, 36)?;
let platform_version = common::string_from_bytes(&versions[4..20]); let platform_version = common::string_from_bytes(&versions[4..20]);
let sdk_version = common::string_from_bytes(&versions[20..36]); let sdk_version = common::string_from_bytes(&versions[20..36]);
+8 -6
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "epk1", detector_func: is_epk1_file, extractor_func: extract_epk1 } Format { name: "epk1", detector_func: is_epk1_file, extractor_func: extract_epk1 }
} }
@@ -41,8 +41,10 @@ struct Pak {
} }
pub fn is_epk1_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_epk1_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let epk2_magic = common::read_file(app_ctx.file, 12, 4)?; //for epk2b let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let epak_magic = common::read_file(app_ctx.file, 0, 4)?;
let epk2_magic = common::read_file(&file, 12, 4)?; //for epk2b
let epak_magic = common::read_file(&file, 0, 4)?;
if epak_magic == b"epak" && epk2_magic != b"EPK2" { if epak_magic == b"epak" && epk2_magic != b"EPK2" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -51,7 +53,7 @@ pub fn is_epk1_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dy
} }
pub fn extract_epk1(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_epk1(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
//check type of epk1 //check type of epk1
let epk1_type; let epk1_type;
let init_pak_count_bytes = common::read_file(&file, 8, 4)?; let init_pak_count_bytes = common::read_file(&file, 8, 4)?;
@@ -125,8 +127,8 @@ pub fn extract_epk1(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<
let data = common::read_exact(&mut file, pak_header.image_size as usize)?; let data = common::read_exact(&mut file, pak_header.image_size as usize)?;
let output_path = Path::new(app_ctx.output_dir).join(pak_header.pak_name() + ".bin"); let output_path = Path::new(&app_ctx.output_dir).join(pak_header.pak_name() + ".bin");
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?; out_file.write_all(&data)?;
+8 -6
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "epk2", detector_func: is_epk2_file, extractor_func: extract_epk2 } Format { name: "epk2", detector_func: is_epk2_file, extractor_func: extract_epk2 }
} }
@@ -72,7 +72,9 @@ struct Pak {
} }
pub fn is_epk2_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_epk2_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 128, 4)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 128, 4)?;
if header == b"epak" { if header == b"epak" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -81,9 +83,9 @@ pub fn is_epk2_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dy
} }
pub fn extract_epk2(app_ctx: &AppContext, _: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_epk2(app_ctx: &AppContext, _: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?;
let _header_signature = common::read_exact(&mut file, SIGNATURE_SIZE as usize)?;
let stored_header = common::read_exact(&mut file, 1584)?; //max header size let stored_header = common::read_exact(&mut file, 1584)?; //max header size
let header; let header;
@@ -191,8 +193,8 @@ pub fn extract_epk2(app_ctx: &AppContext, _: Option<Box<dyn Any>>) -> Result<(),
let segment_data = common::read_exact(&mut file, actual_segment_size as usize)?; 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 out_data = decrypt_aes_ecb_auto(&matching_key_bytes, &segment_data)?;
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", pak.name)); let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", pak.name));
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?; let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?; out_file.write_all(&out_data)?;
+9 -7
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "epk2b", detector_func: is_epk2b_file, extractor_func: extract_epk2b } Format { name: "epk2b", detector_func: is_epk2b_file, extractor_func: extract_epk2b }
} }
@@ -57,8 +57,10 @@ struct Pak {
} }
pub fn is_epk2b_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_epk2b_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let epk2_magic = common::read_file(app_ctx.file, 12, 4)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let epak_magic = common::read_file(app_ctx.file, 0, 4)?;
let epk2_magic = common::read_file(&file, 12, 4)?;
let epak_magic = common::read_file(&file, 0, 4)?;
if epak_magic == b"epak" && epk2_magic == b"EPK2" { if epak_magic == b"epak" && epk2_magic == b"EPK2" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -67,9 +69,9 @@ pub fn is_epk2b_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<d
} }
pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let header: EpkHeader = file.read_le()?;
let header: EpkHeader = file.read_le()?;
println!("EPK info -\nData size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}", println!("EPK info -\nData size: {}\nPak count: {}\nOTA ID: {}\nVersion: {:02x?}.{:02x?}.{:02x?}",
header.file_size, header.pak_count, header.ota_id(), header.version[2], header.version[1], header.version[0]); header.file_size, header.pak_count, header.ota_id(), header.version[2], header.version[1], header.version[0]);
@@ -114,8 +116,8 @@ pub fn extract_epk2b(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result
pak_header.segment_size pak_header.segment_size
}; };
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", pak_header.pak_name())); let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", pak_header.pak_name()));
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?; let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?;
out_file.write_all(&out_data[..segment_limit as usize])?; out_file.write_all(&out_data[..segment_limit as usize])?;
+4 -4
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "epk3", detector_func: is_epk3_file, extractor_func: extract_epk3 } Format { name: "epk3", detector_func: is_epk3_file, extractor_func: extract_epk3 }
} }
@@ -81,7 +81,7 @@ pub fn is_epk3_file(_app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<d
} }
pub fn extract_epk3(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_epk3(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
file.seek(SeekFrom::Start(0))?; file.seek(SeekFrom::Start(0))?;
let stored_header = common::read_exact(&mut file, 1712)?; let stored_header = common::read_exact(&mut file, 1712)?;
let header: Vec<u8>; let header: Vec<u8>;
@@ -164,8 +164,8 @@ pub fn extract_epk3(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<
let encrypted_data = common::read_exact(&mut file, entry.segment_size as usize + extra_segment_size)?; 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 out_data = decrypt_aes_ecb_auto(matching_key_bytes, &encrypted_data)?;
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", entry.package_name())); let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", entry.package_name()));
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?; let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?;
out_file.write_all(&out_data[extra_segment_size..])?; out_file.write_all(&out_data[extra_segment_size..])?;
+7 -5
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "funai_upg", detector_func: is_funai_upg_file, extractor_func: extract_funai_upg } Format { name: "funai_upg", detector_func: is_funai_upg_file, extractor_func: extract_funai_upg }
} }
@@ -26,7 +26,8 @@ struct Entry {
} }
pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 6)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 6)?;
if header == b"UPG\x00\x00\x00" { if header == b"UPG\x00\x00\x00" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -35,7 +36,8 @@ pub fn is_funai_upg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, B
} }
pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let header: Header = file.read_le()?; let header: Header = file.read_le()?;
println!("File info:\nFile size: {}\nEntry count: {}", header.file_size, header.entry_count); println!("File info:\nFile size: {}\nEntry count: {}", header.file_size, header.entry_count);
@@ -51,9 +53,9 @@ pub fn extract_funai_upg(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Re
println!("Descriptor entry info:\n{}", entry_string); println!("Descriptor entry info:\n{}", entry_string);
} }
let output_path = Path::new(app_ctx.output_dir).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(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?; out_file.write_all(&data)?;
+9 -11
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "invincible_image", detector_func: is_invincible_image_file, extractor_func: extract_invincible_image } Format { name: "invincible_image", detector_func: is_invincible_image_file, extractor_func: extract_invincible_image }
} }
@@ -63,7 +63,9 @@ impl Entry {
} }
pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 16)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 16)?;
if header == b"INVINCIBLE_IMAGE" { if header == b"INVINCIBLE_IMAGE" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -72,9 +74,9 @@ pub fn is_invincible_image_file(app_ctx: &AppContext) -> Result<Option<Box<dyn A
} }
pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let header: Header = file.read_le()?;
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: {}", 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: {}",
header.file_version[0], header.file_version[1], header.ver1(), header.ver2(), header.ver3(), header.ver4(), header.data_size, header.data_start_offset, header.keep_size, header.skip_size, header.payload_count); header.file_version[0], header.file_version[1], header.ver1(), header.ver2(), header.ver3(), header.ver4(), header.data_size, header.data_start_offset, header.keep_size, header.skip_size, header.payload_count);
@@ -120,14 +122,10 @@ pub fn extract_invincible_image(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>
println!("\n({}/{}) - {}, Size: {}", i, header.payload_count, entry.name(), entry.size); println!("\n({}/{}) - {}, Size: {}", i, header.payload_count, entry.name(), entry.size);
let data = common::read_exact(&mut data_reader, entry.size as usize)?; let data = common::read_exact(&mut data_reader, entry.size as usize)?;
let output_path = Path::new(app_ctx.output_dir).join(entry.name() + ".bin"); let output_path = Path::new(&app_ctx.output_dir).join(entry.name() + ".bin");
fs::create_dir_all(app_ctx.output_dir)?;
let mut out_file = OpenOptions::new()
.write(true)
.create(true)
.open(output_path)?;
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)?; out_file.write_all(&data)?;
println!("- Saved file!"); println!("- Saved file!");
+10 -7
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "msd10", detector_func: is_msd10_file, extractor_func: extract_msd10 } Format { name: "msd10", detector_func: is_msd10_file, extractor_func: extract_msd10 }
} }
@@ -48,7 +48,9 @@ struct Section {
} }
pub fn is_msd10_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_msd10_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 6)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 6)?;
if header == b"MSDU10" { if header == b"MSDU10" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -57,7 +59,8 @@ pub fn is_msd10_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<d
} }
pub fn extract_msd10(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_msd10(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let header: FileHeader = file.read_le()?; let header: FileHeader = file.read_le()?;
println!("\nNumber of sections: {}", header.section_count); println!("\nNumber of sections: {}", header.section_count);
@@ -138,8 +141,8 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result
out_data = stored_data; out_data = stored_data;
} }
let output_path = Path::new(app_ctx.output_dir).join(item.name.clone()); let output_path = Path::new(&app_ctx.output_dir).join(item.name.clone());
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?; out_file.write_all(&out_data)?;
@@ -183,8 +186,8 @@ pub fn extract_msd10(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result
out_data = stored_data; out_data = stored_data;
} }
let output_path = Path::new(app_ctx.output_dir).join(item.name.clone()); let output_path = Path::new(&app_ctx.output_dir).join(item.name.clone());
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?; out_file.write_all(&out_data)?;
+8 -5
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "msd11", detector_func: is_msd11_file, extractor_func: extract_msd11 } Format { name: "msd11", detector_func: is_msd11_file, extractor_func: extract_msd11 }
} }
@@ -49,7 +49,9 @@ struct Section {
} }
pub fn is_msd11_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_msd11_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 6)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 6)?;
if header == b"MSDU11" { if header == b"MSDU11" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -58,7 +60,8 @@ pub fn is_msd11_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<d
} }
pub fn extract_msd11(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_msd11(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let header: FileHeader = file.read_le()?; let header: FileHeader = file.read_le()?;
println!("\nNumber of sections: {}", header.section_count); println!("\nNumber of sections: {}", header.section_count);
@@ -133,8 +136,8 @@ pub fn extract_msd11(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result
out_data = stored_data; out_data = stored_data;
} }
let output_path = Path::new(app_ctx.output_dir).join(item.name.clone()); let output_path = Path::new(&app_ctx.output_dir).join(item.name.clone());
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?; out_file.write_all(&out_data)?;
+7 -6
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "mstar", detector_func: is_mstar_file, extractor_func: extract_mstar } Format { name: "mstar", detector_func: is_mstar_file, extractor_func: extract_mstar }
} }
@@ -14,9 +14,10 @@ use crate::utils::lzop::{unlzop_to_file};
use crate::utils::sparse::{unsparse_to_file}; use crate::utils::sparse::{unsparse_to_file};
pub fn is_mstar_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_mstar_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 32768)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header_string = String::from_utf8_lossy(&header);
let header = common::read_file(&file, 0, 32768)?;
let header_string = String::from_utf8_lossy(&header);
if header_string.contains("filepartload"){ if header_string.contains("filepartload"){
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -33,7 +34,7 @@ fn parse_number(s: &str) -> Option<u64> {
} }
pub fn extract_mstar(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_mstar(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let file = app_ctx.file; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let mut script = common::read_file(&file, 0, 32768)?; let mut script = common::read_file(&file, 0, 32768)?;
@@ -137,7 +138,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result
} else { } else {
let data = common::read_file(&file, offset, size.try_into().unwrap())?; let data = common::read_file(&file, offset, size.try_into().unwrap())?;
let out_data; let out_data;
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", partname)); let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", partname));
if compression == "lzma" { if compression == "lzma" {
println!("- Decompressing LZMA..."); println!("- Decompressing LZMA...");
@@ -166,7 +167,7 @@ pub fn extract_mstar(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result
out_data = data; out_data = data;
} }
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?; let mut out_file = OpenOptions::new().append(true).create(true).open(output_path)?;
out_file.write_all(&out_data)?; out_file.write_all(&out_data)?;
println!("-- Saved file!"); println!("-- Saved file!");
+6 -5
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "mtk_bdp", detector_func: is_mtk_bdp_file, extractor_func: extract_mtk_bdp } Format { name: "mtk_bdp", detector_func: is_mtk_bdp_file, extractor_func: extract_mtk_bdp }
} }
@@ -78,7 +78,7 @@ fn find_bytes(data: &[u8], pattern: &[u8]) -> Option<usize> {
} }
pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let file_size = file.metadata()?.len(); let file_size = file.metadata()?.len();
let mut data = Vec::new(); let mut data = Vec::new();
@@ -96,8 +96,9 @@ pub fn is_mtk_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box
} }
pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let ctx = ctx.and_then(|c: Box<dyn Any>| c.downcast::<MtkBdpContext>().ok()).ok_or("Context is invalid or missing!")?; 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; let offset = ctx.pitit_offset;
println!("\nReading PITIT at: {}", offset); println!("\nReading PITIT at: {}", offset);
@@ -181,8 +182,8 @@ pub fn extract_mtk_bdp(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Resul
let data = common::read_file(&file, bit_entry.offset as u64, bit_entry.size as usize)?; let data = common::read_file(&file, bit_entry.offset as u64, bit_entry.size as usize)?;
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", name)); let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", name));
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().read(true).write(true).create(true).open(output_path)?; 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.seek(SeekFrom::Start(bit_entry.offset_in_target_part as u64))?;
out_file.write_all(&data)?; out_file.write_all(&data)?;
+9 -7
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "mtk_pkg", detector_func: is_mtk_pkg_file, extractor_func: extract_mtk_pkg } Format { name: "mtk_pkg", detector_func: is_mtk_pkg_file, extractor_func: extract_mtk_pkg }
} }
@@ -82,13 +82,15 @@ static HEADER_KEY: [u8; 16] = [
static HEADER_IV: [u8; 16] = [0x00; 16]; static HEADER_IV: [u8; 16] = [0x00; 16];
pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let mut encrypted_header = common::read_file(app_ctx.file, 0, HEADER_SIZE)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let mut encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?;
let mut header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?; let mut header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?;
if &header[4..12] == MTK_HEADER_MAGIC { if &header[4..12] == MTK_HEADER_MAGIC {
Ok(Some(Box::new(MtkPkgContext { is_philips_variant: false, decrypted_header: header}))) Ok(Some(Box::new(MtkPkgContext { is_philips_variant: false, decrypted_header: header})))
} else { } else {
// try for philips which has additional 128 bytes at beginning // try for philips which has additional 128 bytes at beginning
encrypted_header = common::read_file(app_ctx.file, PHILIPS_EXTRA_HEADER_SIZE as u64, HEADER_SIZE)?; encrypted_header = common::read_file(&file, PHILIPS_EXTRA_HEADER_SIZE as u64, HEADER_SIZE)?;
header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?; header = decrypt_aes128_cbc_nopad(&encrypted_header, &HEADER_KEY, &HEADER_IV)?;
if &header[4..12] == MTK_HEADER_MAGIC { if &header[4..12] == MTK_HEADER_MAGIC {
Ok(Some(Box::new(MtkPkgContext { is_philips_variant: true, decrypted_header: header }))) Ok(Some(Box::new(MtkPkgContext { is_philips_variant: true, decrypted_header: header })))
@@ -100,7 +102,7 @@ pub fn is_mtk_pkg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box
} }
pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let ctx = ctx.and_then(|c: Box<dyn Any>| c.downcast::<MtkPkgContext>().ok()).ok_or("Context is invalid or missing!")?; 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 file_size = file.metadata()?.len();
@@ -198,13 +200,13 @@ pub fn extract_mtk_pkg(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Resul
}; };
//for compressed part create temp file //for compressed part create temp file
let output_path = Path::new(app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); 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)?; 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)?; 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..])?; out_file.write_all(&out_data[CRYPTED_HEADER_SIZE + extra_header_len as usize..])?;
if part_entry.is_compressed() { if part_entry.is_compressed() {
let lzhs_out_path = Path::new(app_ctx.output_dir).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) { match decompress_lzhs_fs_file2file(&out_file, lzhs_out_path) {
Ok(()) => { Ok(()) => {
println!("- Decompressed Successfully!"); println!("- Decompressed Successfully!");
+9 -6
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "mtk_pkg_new", detector_func: is_mtk_pkg_new_file, extractor_func: extract_mtk_pkg_new } Format { name: "mtk_pkg_new", detector_func: is_mtk_pkg_new_file, extractor_func: extract_mtk_pkg_new }
} }
@@ -71,7 +71,9 @@ impl PartEntry {
static HEADER_SIZE: usize = 0x170; static HEADER_SIZE: usize = 0x170;
pub fn is_mtk_pkg_new_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_mtk_pkg_new_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let encrypted_header = common::read_file(app_ctx.file, 0, HEADER_SIZE)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?;
for (key_hex, iv_hex, name) in keys::MTK_PKG_CUST { 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 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 iv_array: [u8; 16] = hex::decode(iv_hex)?.as_slice().try_into()?;
@@ -91,8 +93,9 @@ pub fn is_mtk_pkg_new_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>,
} }
pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let ctx = ctx.and_then(|c: Box<dyn Any>| c.downcast::<MtkPkgNewContext>().ok()).ok_or("Context is invalid or missing!")?; 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(); let file_size = file.metadata()?.len();
//the key was founf, and header was decrypted at detection stage so we can reuse //the key was founf, and header was decrypted at detection stage so we can reuse
@@ -155,13 +158,13 @@ pub fn extract_mtk_pkg_new(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> R
}; };
//for compressed part create temp file //for compressed part create temp file
let output_path = Path::new(app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); 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)?; 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)?; 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..])?; out_file.write_all(&out_data[48 + extra_header_len as usize..])?;
if part_entry.is_compressed() { if part_entry.is_compressed() {
let lzhs_out_path = Path::new(app_ctx.output_dir).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) { match decompress_lzhs_fs_file2file(&out_file, lzhs_out_path) {
Ok(()) => { Ok(()) => {
println!("-- Decompressed Successfully!"); println!("-- Decompressed Successfully!");
+8 -6
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "mtk_pkg_old", detector_func: is_mtk_pkg_old_file, extractor_func: extract_mtk_pkg_old } Format { name: "mtk_pkg_old", detector_func: is_mtk_pkg_old_file, extractor_func: extract_mtk_pkg_old }
} }
@@ -64,7 +64,8 @@ impl PartEntry {
static HEADER_SIZE: usize = 0x98; static HEADER_SIZE: usize = 0x98;
pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?; let encrypted_header = common::read_file(&file, 0, HEADER_SIZE)?;
let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK)); let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK));
if &header[4..12] == MTK_HEADER_MAGIC { if &header[4..12] == MTK_HEADER_MAGIC {
@@ -83,7 +84,8 @@ pub fn is_mtk_pkg_old_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>,
} }
pub fn extract_mtk_pkg_old(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_mtk_pkg_old(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let file_size = file.metadata()?.len(); let file_size = file.metadata()?.len();
let encrypted_header = common::read_exact(&mut file, HEADER_SIZE)?; let encrypted_header = common::read_exact(&mut file, HEADER_SIZE)?;
let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK)); let header = decrypt(&encrypted_header, KEY, Some(HEADER_XOR_MASK));
@@ -126,13 +128,13 @@ pub fn extract_mtk_pkg_old(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) ->
}; };
//for compressed part create temp file //for compressed part create temp file
let output_path = Path::new(app_ctx.output_dir).join(part_entry.name() + if part_entry.is_compressed() {".lzhs"} else {".bin"}); 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)?; 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)?; 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..])?; out_file.write_all(&out_data[extra_header_len as usize..])?;
if part_entry.is_compressed() { if part_entry.is_compressed() {
let lzhs_out_path = Path::new(app_ctx.output_dir).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) { match decompress_lzhs_fs_file2file_old(&out_file, lzhs_out_path) {
Ok(()) => { Ok(()) => {
println!("- Decompressed Successfully!"); println!("- Decompressed Successfully!");
+9 -11
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "novatek", detector_func: is_novatek_file, extractor_func: extract_novatek } Format { name: "novatek", detector_func: is_novatek_file, extractor_func: extract_novatek }
} }
@@ -41,7 +41,9 @@ struct PartEntry {
} }
pub fn is_novatek_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_novatek_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 4)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 4)?;
if header == b"NFWB" { if header == b"NFWB" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -50,9 +52,9 @@ pub fn is_novatek_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box
} }
pub fn extract_novatek(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_novatek(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let header: Header = file.read_le()?;
let header: Header = file.read_le()?;
println!("File info:\nFirmware name: {}\nVersion: {}.{}\nData size: {}\nPart count: {}", println!("File info:\nFirmware name: {}\nVersion: {}.{}\nData size: {}\nPart count: {}",
header.firmware_name(), header.version_major, header.version_minor, header.data_size, header.part_count); header.firmware_name(), header.version_major, header.version_minor, header.data_size, header.part_count);
@@ -69,14 +71,10 @@ pub fn extract_novatek(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Resu
let data = common::read_file(&file, entry.offset as u64, entry.size as usize)?; let data = common::read_file(&file, entry.offset as u64, entry.size as usize)?;
let output_path = Path::new(app_ctx.output_dir).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(app_ctx.output_dir)?;
let mut out_file = OpenOptions::new()
.write(true)
.create(true)
.open(output_path)?;
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)?; out_file.write_all(&data)?;
println!("- Saved file!"); println!("- Saved file!");
+9 -10
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "nvt_timg", detector_func: is_nvt_timg_file, extractor_func: extract_nvt_timg } Format { name: "nvt_timg", detector_func: is_nvt_timg_file, extractor_func: extract_nvt_timg }
} }
@@ -50,7 +50,9 @@ impl PIMG {
} }
pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 4)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 4)?;
if header == b"TIMG" { if header == b"TIMG" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -59,7 +61,8 @@ pub fn is_nvt_timg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Bo
} }
pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let file_size = file.metadata()?.len(); let file_size = file.metadata()?.len();
let timg: TIMG = file.read_le()?; let timg: TIMG = file.read_le()?;
println!("File info:\nData size: {}", timg.data_size); println!("File info:\nData size: {}", timg.data_size);
@@ -78,7 +81,7 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Res
println!("\n#{} - {}, Size: {}, Dest: {}, Compression: {}", pimg_i, pimg.name(), pimg.size, pimg.dest_dev(), pimg.comp_type()); println!("\n#{} - {}, Size: {}, Dest: {}, Compression: {}", pimg_i, pimg.name(), pimg.size, pimg.dest_dev(), pimg.comp_type());
let out_data; let out_data;
let output_path = Path::new(app_ctx.output_dir).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 if pimg.comp_type() == "gzip" && data.starts_with(b"\x1F\x8B") { //additionally check for gzip header, because sometimes its deceptive
println!("- Decompressing gzip..."); println!("- Decompressing gzip...");
@@ -95,12 +98,8 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Res
out_data = data; out_data = data;
} }
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new() let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
.write(true)
.create(true)
.open(output_path)?;
out_file.write_all(&out_data)?; out_file.write_all(&out_data)?;
println!("-- Saved file!"); println!("-- Saved file!");
+7 -6
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "pana_dvd", detector_func: is_pana_dvd_file, extractor_func: extract_pana_dvd } Format { name: "pana_dvd", detector_func: is_pana_dvd_file, extractor_func: extract_pana_dvd }
} }
@@ -150,7 +150,8 @@ pub fn find_aes_key_pair<'a>(key_array: &'a [(&'a str, &'a str, &'a str)], data:
} }
pub fn is_pana_dvd_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_pana_dvd_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 64)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 64)?;
if let Some(matching_key) = find_key(&keys::PANA_DVD_KEYONLY, &header, b"PROG", 0)? { if let Some(matching_key) = find_key(&keys::PANA_DVD_KEYONLY, &header, b"PROG", 0)? {
Ok(Some(Box::new(PanaDvdContext { Ok(Some(Box::new(PanaDvdContext {
matching_key: matching_key, matching_key: matching_key,
@@ -181,7 +182,7 @@ pub fn is_pana_dvd_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Bo
} }
pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let context = ctx.and_then(|c: Box<dyn Any>| c.downcast::<PanaDvdContext>().ok()).ok_or("Context is invalid or missing!")?; 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 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
@@ -217,12 +218,12 @@ pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Resu
if file_entries.len() == 1 { if file_entries.len() == 1 {
//only one file, standard extraction //only one file, standard extraction
println!("File contains no extra sub-files...\n"); 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, app_ctx.output_dir)?; 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 { } else {
println!("File contains {} sub-files...", file_entries.len()); println!("File contains {} sub-files...", file_entries.len());
for (i, file_entry ) in file_entries.iter().enumerate() { 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); 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_{}", app_ctx.output_dir, i + 1))?; extract_file(&mut file_reader, file_entry.offset as u64, file_entry.base_offset as u64, matching_key, &app_ctx.output_dir.join(format!("file_{}", i + 1)))?;
} }
} }
@@ -231,7 +232,7 @@ pub fn extract_pana_dvd(app_ctx: &AppContext, ctx: Option<Box<dyn Any>>) -> Resu
Ok(()) Ok(())
} }
fn extract_file(file_reader: &mut Cursor<Vec<u8>>, offset: u64, base_offset: u64, key: [u8; 8], output_folder: &str) -> Result<(), Box<dyn std::error::Error>> { fn extract_file(file_reader: &mut Cursor<Vec<u8>>, offset: u64, base_offset: u64, key: [u8; 8], output_folder: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
file_reader.seek(SeekFrom::Start(offset + base_offset))?; file_reader.seek(SeekFrom::Start(offset + base_offset))?;
let enc_header = common::read_exact(file_reader, MAX_HEADER_SIZE)?; let enc_header = common::read_exact(file_reader, MAX_HEADER_SIZE)?;
+10 -10
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "pfl_upg", detector_func: is_pfl_upg_file, extractor_func: extract_pfl_upg } Format { name: "pfl_upg", detector_func: is_pfl_upg_file, extractor_func: extract_pfl_upg }
} }
@@ -49,7 +49,9 @@ impl FileHeader {
} }
pub fn is_pfl_upg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_pfl_upg_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 8)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 8)?;
if header == b"2SWU3TXV" { if header == b"2SWU3TXV" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -90,7 +92,8 @@ fn decrypt_aes256_ecb(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, Box<dyn
} }
pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let header: Header = file.read_le()?; let header: Header = file.read_le()?;
let signature = common::read_exact(&mut file, 128)?; let signature = common::read_exact(&mut file, 128)?;
let _ = common::read_exact(&mut file, 32)?; //unknown let _ = common::read_exact(&mut file, 32)?; //unknown
@@ -163,7 +166,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Resu
//its a folder not a file //its a folder not a file
if (file_header.attributes[3] & (1 << 1)) != 0 { if (file_header.attributes[3] & (1 << 1)) != 0 {
println!("\nFolder - {}", file_header.file_name()); println!("\nFolder - {}", file_header.file_name());
let output_path = Path::new(app_ctx.output_dir).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)?; fs::create_dir_all(output_path)?;
continue continue
} }
@@ -181,7 +184,7 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Resu
println!("\nFile - {}, Size: {}", file_name, file_header.real_size); println!("\nFile - {}, Size: {}", file_name, file_header.real_size);
let data = common::read_exact(&mut data_reader, file_header.stored_size as usize)?; let data = common::read_exact(&mut data_reader, file_header.stored_size as usize)?;
let output_path = Path::new(app_ctx.output_dir).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!"); let output_path_parent = output_path.parent().expect("Failed to get parent of the output path!");
//prevent collisions //prevent collisions
@@ -194,11 +197,8 @@ pub fn extract_pfl_upg(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Resu
fs::create_dir_all(parent)?; fs::create_dir_all(parent)?;
} }
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new() let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
.write(true)
.create(true)
.open(output_path)?;
out_file.write_all(&data[..file_header.real_size as usize])?; out_file.write_all(&data[..file_header.real_size as usize])?;
+12 -18
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "pup", detector_func: is_pup_file, extractor_func: extract_pup } Format { name: "pup", detector_func: is_pup_file, extractor_func: extract_pup }
} }
@@ -57,7 +57,9 @@ struct BlockEntry {
} }
pub fn is_pup_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_pup_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 4)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 4)?;
if header == b"\x4F\x15\x3D\x1D" || header == b"\x54\x14\xF5\xEE" { //ps4, ps5 if header == b"\x4F\x15\x3D\x1D" || header == b"\x54\x14\xF5\xEE" { //ps4, ps5
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -66,9 +68,9 @@ pub fn is_pup_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn
} }
pub fn extract_pup(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_pup(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let header: Header = file.read_le()?;
let header: Header = file.read_le()?;
println!("File info:\nFile size: {}\nEntry count: {}", println!("File info:\nFile size: {}\nEntry count: {}",
header.file_size, header.entry_count); header.file_size, header.entry_count);
@@ -139,14 +141,10 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(
out_data = data; out_data = data;
} }
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", entry.id())); let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", entry.id()));
fs::create_dir_all(app_ctx.output_dir)?;
let mut out_file = OpenOptions::new()
.append(true)
.create(true)
.open(output_path)?;
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)?; out_file.write_all(&out_data)?;
println!("-- Saved!"); println!("-- Saved!");
@@ -165,14 +163,10 @@ pub fn extract_pup(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(
out_data = data; out_data = data;
} }
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", entry.id())); let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", entry.id()));
fs::create_dir_all(app_ctx.output_dir)?;
let mut out_file = OpenOptions::new()
.write(true)
.create(true)
.open(output_path)?;
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)?; out_file.write_all(&out_data)?;
println!("-- Saved file!"); println!("-- Saved file!");
+9 -6
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "roku", detector_func: is_roku_file, extractor_func: extract_roku } Format { name: "roku", detector_func: is_roku_file, extractor_func: extract_roku }
} }
@@ -62,7 +62,9 @@ impl ImageHeader {
} }
pub fn is_roku_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_roku_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 32)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 32)?;
let try_decrypt_header = decrypt_aes128_cbc_nopad(&header, &FILE_KEY, &FILE_IV)?; 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") { if try_decrypt_header.starts_with(b"manifest\x00\x00\x00\x00\x00\x00\x00\x00") {
@@ -73,7 +75,8 @@ pub fn is_roku_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dy
} }
pub fn extract_roku(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_roku(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let mut encrypted_data = Vec::new(); let mut encrypted_data = Vec::new();
file.read_to_end(&mut encrypted_data)?; file.read_to_end(&mut encrypted_data)?;
@@ -116,7 +119,7 @@ pub fn extract_roku(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<
common::read_exact(&mut image_reader, image.size1 as usize - image.data_start_offset as usize)? common::read_exact(&mut image_reader, image.size1 as usize - image.data_start_offset as usize)?
}; };
let folder_path = Path::new(app_ctx.output_dir).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())); let output_path = Path::new(&folder_path).join(format!("{}_{}.bin", i, image.type_string()));
fs::create_dir_all(&folder_path)?; fs::create_dir_all(&folder_path)?;
@@ -130,9 +133,9 @@ pub fn extract_roku(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<
} else { } else {
println!("\nOther/Unknown file: {:?}", path); println!("\nOther/Unknown file: {:?}", path);
let output_path = Path::new(app_ctx.output_dir).join(&path); let output_path = Path::new(&app_ctx.output_dir).join(&path);
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&contents)?; out_file.write_all(&contents)?;
+12 -12
View File
@@ -1,10 +1,10 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "ruf", detector_func: is_ruf_file, extractor_func: extract_ruf } Format { name: "ruf", detector_func: is_ruf_file, extractor_func: extract_ruf }
} }
use std::path::{Path}; use std::path::{Path, PathBuf};
use std::fs::{self, File, OpenOptions}; use std::fs::{self, File, OpenOptions};
use binrw::{BinRead, BinReaderExt}; use binrw::{BinRead, BinReaderExt};
use std::io::{Write, Seek, SeekFrom, Cursor}; use std::io::{Write, Seek, SeekFrom, Cursor};
@@ -76,7 +76,9 @@ impl RufEntry {
} }
pub fn is_ruf_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_ruf_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 6)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 6)?;
if header == b"RUF\x00\x00\x00" { if header == b"RUF\x00\x00\x00" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -85,22 +87,23 @@ pub fn is_ruf_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn
} }
pub fn extract_ruf(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_ruf(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let header: RufHeader = file.read_be()?; let header: RufHeader = file.read_be()?;
if header.is_dual_ruf() { if header.is_dual_ruf() {
println!("\nDual RUF detected! Extracting 1st RUF...\n"); println!("\nDual RUF detected! Extracting 1st RUF...\n");
actually_extract_ruf(file, &format!("{}/RUF_1", app_ctx.output_dir), 0)?; actually_extract_ruf(file, &app_ctx.output_dir.join("RUF_1"), 0)?;
println!("\nExtracting 2nd RUF...\n"); println!("\nExtracting 2nd RUF...\n");
actually_extract_ruf(file, &format!("{}/RUF_2", app_ctx.output_dir), 41943088)?; actually_extract_ruf(file, &app_ctx.output_dir.join("RUF_2"), 41943088)?;
} else { } else {
actually_extract_ruf(file, app_ctx.output_dir, 0)?; actually_extract_ruf(file, &app_ctx.output_dir, 0)?;
} }
println!("\nExtraction finished!"); println!("\nExtraction finished!");
Ok(()) Ok(())
} }
fn actually_extract_ruf(mut file: &File, output_folder: &str, start_offset: u64) -> Result<(), Box<dyn std::error::Error>> { fn actually_extract_ruf(mut file: &File, output_folder: &PathBuf, start_offset: u64) -> Result<(), Box<dyn std::error::Error>> {
file.seek(SeekFrom::Start(start_offset))?; file.seek(SeekFrom::Start(start_offset))?;
let header: RufHeader = file.read_be()?; let header: RufHeader = file.read_be()?;
@@ -166,10 +169,7 @@ fn actually_extract_ruf(mut file: &File, output_folder: &str, start_offset: u64)
let output_path = Path::new(&output_folder).join(format!("{}_{}.bin", entry.payload_type_bytes, entry.payload_type())); let output_path = Path::new(&output_folder).join(format!("{}_{}.bin", entry.payload_type_bytes, entry.payload_type()));
fs::create_dir_all(&output_folder)?; fs::create_dir_all(&output_folder)?;
let mut out_file = OpenOptions::new() let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
.write(true)
.create(true)
.open(output_path)?;
out_file.write_all(&data)?; out_file.write_all(&data)?;
+8 -7
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "rvp", detector_func: is_rvp_file, extractor_func: extract_rvp } Format { name: "rvp", detector_func: is_rvp_file, extractor_func: extract_rvp }
} }
@@ -19,16 +19,16 @@ fn decrypt_xor(data: &[u8]) -> Vec<u8> {
} }
pub fn is_rvp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_rvp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
//MVP //MVP
let header = common::read_file(file, 0, 4)?; let header = common::read_file(&file, 0, 4)?;
if header == b"UPDT" { if header == b"UPDT" {
file.seek(std::io::SeekFrom::Start(36))?; //skip rest of header // NOT GOOD PRACTICE SHOULD BE REMOVED file.seek(std::io::SeekFrom::Start(36))?; //skip rest of header // NOT GOOD PRACTICE SHOULD BE REMOVED
return Ok(Some(Box::new(()))) return Ok(Some(Box::new(())))
} }
//RVP //RVP
let bytes = common::read_file(file, 16, 18)?; let bytes = common::read_file(&file, 16, 18)?;
for (_i, &b) in bytes.iter().enumerate().step_by(2) { for (_i, &b) in bytes.iter().enumerate().step_by(2) {
if b != 0xA3 { if b != 0xA3 {
return Ok(None); return Ok(None);
@@ -40,7 +40,8 @@ pub fn is_rvp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn
} }
pub fn extract_rvp(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_rvp(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let mut obf_data = Vec::new(); //we sadly cannot deXOR on the fly because of its 32 byte pattern 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)?; file.read_to_end(&mut obf_data)?;
println!("DeXORing data.."); println!("DeXORing data..");
@@ -122,9 +123,9 @@ pub fn extract_rvp(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(
println!("Size: {}", size); println!("Size: {}", size);
let data = common::read_exact(&mut data_reader, size as usize)?; let data = common::read_exact(&mut data_reader, size as usize)?;
let output_path = Path::new(app_ctx.output_dir).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(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?; out_file.write_all(&data)?;
+17 -8
View File
@@ -1,5 +1,11 @@
use std::any::Any;
use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format {
Format { name: "samsung_old", detector_func: is_samsung_old_dir, extractor_func: extract_samsung_old }
}
use std::fs; use std::fs;
use std::path::{Path, PathBuf}; use std::path::{Path};
use std::fs::{File, OpenOptions}; use std::fs::{File, OpenOptions};
use std::io::{Write}; use std::io::{Write};
use hex::decode; use hex::decode;
@@ -11,11 +17,13 @@ use crate::utils::aes::{decrypt_aes128_cbc_pcks7};
use md5; use md5;
pub fn is_samsung_old_dir(path: &PathBuf) -> bool { pub fn is_samsung_old_dir(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
if Path::new(&path).join("image").is_dir() & Path::new(&path).join("image/info.txt").exists(){ let dir = match &app_ctx.input {InputTarget::Directory(p) => p, InputTarget::File(_) => return Ok(None)};
true
if Path::new(&dir).join("image").is_dir() & Path::new(&dir).join("image/info.txt").exists(){
Ok(Some(Box::new(())))
} else { } else {
false Ok(None)
} }
} }
@@ -27,7 +35,8 @@ fn decrypt_xor(data: &[u8], key: &str) -> Vec<u8> {
.collect() .collect()
} }
pub fn extract_samsung_old(path: &PathBuf, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_samsung_old(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let path = match &app_ctx.input {InputTarget::Directory(p) => p, InputTarget::File(_) => return Err("Extractor expected directory, not file".into())};
let fw_info = fs::read_to_string(Path::new(&path).join("image/info.txt"))?; let fw_info = fs::read_to_string(Path::new(&path).join("image/info.txt"))?;
println!("Firmware info: {}", fw_info); println!("Firmware info: {}", fw_info);
@@ -114,9 +123,9 @@ pub fn extract_samsung_old(path: &PathBuf, output_folder: &str) -> Result<(), Bo
let xor_key = fw_info.split_whitespace().next().unwrap(); let xor_key = fw_info.split_whitespace().next().unwrap();
let out_data = decrypt_xor(&decrypted_data, xor_key); let out_data = decrypt_xor(&decrypted_data, xor_key);
let output_path = Path::new(&output_folder).join(filename.rsplit_once('.').map(|(left, _)| left).unwrap()); let output_path = Path::new(&app_ctx.output_dir).join(filename.rsplit_once('.').map(|(left, _)| left).unwrap());
fs::create_dir_all(&output_folder)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new() let mut out_file = OpenOptions::new()
.write(true) .write(true)
.create(true) .create(true)
+10 -7
View File
@@ -1,6 +1,6 @@
//sddl_dec 5.0 //sddl_dec 5.0
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "sddl_sec", detector_func: is_sddl_sec_file, extractor_func: extract_sddl_sec } Format { name: "sddl_sec", detector_func: is_sddl_sec_file, extractor_func: extract_sddl_sec }
} }
@@ -104,7 +104,9 @@ static DEC_IV: [u8; 16] = [
]; ];
pub fn is_sddl_sec_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_sddl_sec_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 32)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 32)?;
let deciph_header = decipher(&header); let deciph_header = decipher(&header);
if deciph_header.starts_with(b"\x11\x22\x33\x44") { if deciph_header.starts_with(b"\x11\x22\x33\x44") {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
@@ -156,7 +158,8 @@ fn decipher(s: &[u8]) -> Vec<u8> {
} }
pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let mut hdr_reader = Cursor::new(decipher(&common::read_exact(&mut file, 32)?)); let mut hdr_reader = Cursor::new(decipher(&common::read_exact(&mut file, 32)?));
let hdr: SddlSecHeader = hdr_reader.read_be()?; let hdr: SddlSecHeader = hdr_reader.read_be()?;
@@ -174,10 +177,10 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Res
let data = common::read_exact(&mut file, entry_header.size() as usize)?; let data = common::read_exact(&mut file, entry_header.size() as usize)?;
let dec_data = decrypt_aes128_cbc_pcks7(&data, &DEC_KEY, &DEC_IV)?; let dec_data = decrypt_aes128_cbc_pcks7(&data, &DEC_KEY, &DEC_IV)?;
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
//detect the file type based on the counts of each file //detect the file type based on the counts of each file
if i == 0 { //SDIT.FDI file if i == 0 { //SDIT.FDI file
let output_path = Path::new(app_ctx.output_dir).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)?; let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&dec_data)?; out_file.write_all(&dec_data)?;
println!("-- Saved file!"); println!("-- Saved file!");
@@ -216,11 +219,11 @@ pub fn extract_sddl_sec(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Res
let file_name = common::string_from_bytes(&file_name_bytes); let file_name = common::string_from_bytes(&file_name_bytes);
println!("--- File name: {}", file_name); println!("--- File name: {}", file_name);
let out_folder_path = Path::new(app_ctx.output_dir).join(source_name); let out_folder_path = Path::new(&app_ctx.output_dir).join(source_name);
fs::create_dir_all(&out_folder_path)?; fs::create_dir_all(&out_folder_path)?;
output_path = Path::new(&out_folder_path).join(file_name); output_path = Path::new(&out_folder_path).join(file_name);
} else { } else {
output_path = Path::new(app_ctx.output_dir).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)?; let data = common::read_exact(&mut content_reader, content_header.size as usize)?;
+8 -6
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "slp", detector_func: is_slp_file, extractor_func: extract_slp } Format { name: "slp", detector_func: is_slp_file, extractor_func: extract_slp }
} }
@@ -53,7 +53,9 @@ struct EntryNew {
} }
pub fn is_slp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_slp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 4)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&file, 0, 4)?;
if header == b"SLP\x00" { if header == b"SLP\x00" {
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -62,9 +64,9 @@ pub fn is_slp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn
} }
pub fn extract_slp(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_slp(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let header: Header = file.read_le()?;
let header: Header = file.read_le()?;
println!("File info:\nModel: {}\nVersion: {}\nFirmware: {}\nNew type: {}\n", println!("File info:\nModel: {}\nVersion: {}\nFirmware: {}\nNew type: {}\n",
header.model(), header.version(), header.firmware(), header.is_new_type()); header.model(), header.version(), header.firmware(), header.is_new_type());
@@ -99,9 +101,9 @@ pub fn extract_slp(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(
file.seek(SeekFrom::Start(entry.offset.into()))?; file.seek(SeekFrom::Start(entry.offset.into()))?;
let data = common::read_exact(&mut file, entry.size as usize)?; let data = common::read_exact(&mut file, entry.size as usize)?;
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", i)); let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", i));
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new() let mut out_file = OpenOptions::new()
.write(true) .write(true)
.create(true) .create(true)
+10 -7
View File
@@ -1,5 +1,5 @@
use std::any::Any; use std::any::Any;
use crate::{AppContext, formats::Format}; use crate::{InputTarget, AppContext, formats::Format};
pub fn format() -> Format { pub fn format() -> Format {
Format { name: "sony_bdp", detector_func: is_sony_bdp_file, extractor_func: extract_sony_bdp } Format { name: "sony_bdp", detector_func: is_sony_bdp_file, extractor_func: extract_sony_bdp }
} }
@@ -64,7 +64,9 @@ struct Entry {
} }
pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> { pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
let header = common::read_file(app_ctx.file, 0, 4)?; let file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Ok(None)};
let header = common::read_file(&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 if header == b"\x01\x73\xEC\xC9" || header == b"\x01\x73\xEC\x1F" || header == b"\xEC\x7D\xB0\xB0" { //MSB1x, MSB0x, BDPPxx
Ok(Some(Box::new(()))) Ok(Some(Box::new(())))
} else { } else {
@@ -73,7 +75,8 @@ pub fn is_sony_bdp_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Bo
} }
pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> { pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error>> {
let mut file = app_ctx.file; let mut file = match &app_ctx.input {InputTarget::File(f) => f, InputTarget::Directory(_) => return Err("Extractor expected file, not directory".into())};
let obf_header = common::read_exact(&mut file, 300)?; let obf_header = common::read_exact(&mut file, 300)?;
let header = hex_substitute(&obf_header); let header = hex_substitute(&obf_header);
let mut hdr_reader = Cursor::new(header); let mut hdr_reader = Cursor::new(header);
@@ -103,10 +106,10 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Res
let obf_data = common::read_file(&file, entry.offset as u64, entry.size as usize)?; let obf_data = common::read_file(&file, entry.offset as u64, entry.size as usize)?;
let data = hex_substitute(&obf_data); let data = hex_substitute(&obf_data);
let output_path = Path::new(app_ctx.output_dir).join(format!("{}.bin", i)); let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", i));
last_file_path = Some(output_path.clone()); last_file_path = Some(output_path.clone());
fs::create_dir_all(app_ctx.output_dir)?; fs::create_dir_all(&app_ctx.output_dir)?;
let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?; let mut out_file = OpenOptions::new().write(true).create(true).open(output_path)?;
out_file.write_all(&data)?; out_file.write_all(&data)?;
@@ -119,8 +122,8 @@ pub fn extract_sony_bdp(app_ctx: &AppContext, _ctx: Option<Box<dyn Any>>) -> Res
println!("\nChecking if it's also MTK BDP..."); println!("\nChecking if it's also MTK BDP...");
let last_file = File::open(last_file_path.unwrap())?; let last_file = File::open(last_file_path.unwrap())?;
let mtk_extraction_path = format!("{}/{}", app_ctx.output_dir, i - 1); let mtk_extraction_path = app_ctx.output_dir.join(format!("{}", i + 1));
let ctx: AppContext = AppContext { file: &last_file, output_dir: &mtk_extraction_path }; let ctx: AppContext = AppContext { input: InputTarget::File(last_file), output_dir: mtk_extraction_path };
if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&ctx)? { if let Some(result) = formats::mtk_bdp::is_mtk_bdp_file(&ctx)? {
println!("- MTK BDP file detected!\n"); println!("- MTK BDP file detected!\n");
+10 -10
View File
@@ -2,16 +2,16 @@
//samsung old keys //samsung old keys
//github.com/george-hopkins/samygo-patcher //github.com/george-hopkins/samygo-patcher
//pub static SAMSUNG: &[(&str, &str)] = &[ pub static SAMSUNG: &[(&str, &str)] = &[
// ("T-GA", "SHWJUH:85a045ae-2296-484c-b457-ede832fcfbe1-646390a3-105e-40aa-85f6-da3086c70111"), ("T-GA", "SHWJUH:85a045ae-2296-484c-b457-ede832fcfbe1-646390a3-105e-40aa-85f6-da3086c70111"),
// ("T-MST5", "SHWJUH:eceb2c14-db11-425e-9ebf-5f9607f0eb4b-3c38193e-751e-4719-8884-9e76322c0cec"), ("T-MST5", "SHWJUH:eceb2c14-db11-425e-9ebf-5f9607f0eb4b-3c38193e-751e-4719-8884-9e76322c0cec"),
// ("T-MST10P","b4c136-fbc93576-b3e8-4035-bf4e-ba4cb4ada1ac-f0d81cc4-8301-4832-bd60-f331295743ba"), ("T-MST10P","b4c136-fbc93576-b3e8-4035-bf4e-ba4cb4ada1ac-f0d81cc4-8301-4832-bd60-f331295743ba"),
// ("T-VAL", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00001abc2010"), ("T-VAL", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00001abc2010"),
// ("T-TDT", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00002abc2010"), ("T-TDT", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00002abc2010"),
// ("T-MSX", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00004abc2010"), ("T-MSX", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-00004abc2010"),
// ("T-CH", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-611c4f8d4a71"), ("T-CH", "A435HX:d3e90afc-0f09-4054-9bac-350cc8dfc901-7cee72ea-15ae-45ce-b0f5-611c4f8d4a71"),
// ("T-ECP", "3EF6067262CF0C678598BFF22169D1F1EA57C284"), ("T-ECP", "3EF6067262CF0C678598BFF22169D1F1EA57C284"),
//]; ];
//MSD10 keys //MSD10 keys
//fw prefix, type, key //fw prefix, type, key
+28 -7
View File
@@ -14,9 +14,14 @@ struct Args {
output_directory: Option<String>, output_directory: Option<String>,
} }
pub struct AppContext<'a> { pub enum InputTarget {
pub file: &'a std::fs::File, File(File),
pub output_dir: &'a str, Directory(PathBuf),
}
pub struct AppContext {
pub input: InputTarget,
pub output_dir: PathBuf,
} }
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -32,24 +37,40 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} else { } else {
format!("_{}", target_path.file_name().and_then(|s| s.to_str()).unwrap()) format!("_{}", target_path.file_name().and_then(|s| s.to_str()).unwrap())
}; };
println!("Output directory: {}\n", output_path_str); println!("Output directory: {}", output_path_str);
let output_directory_path = PathBuf::from(&output_path_str); let output_directory_path = PathBuf::from(&output_path_str);
if output_directory_path.exists() { if output_directory_path.exists() {
if output_directory_path.is_dir() { if output_directory_path.is_dir() {
let is_empty = fs::read_dir(&output_directory_path)?.next().is_none(); let is_empty = fs::read_dir(&output_directory_path)?.next().is_none();
if !is_empty { if !is_empty {
println!("Warning: Output folder already exists and is NOT empty! Files may be overwritten!"); println!("\nWarning: Output folder already exists and is NOT empty! Files may be overwritten!");
println!("Press Enter if you want to continue..."); println!("Press Enter if you want to continue...");
io::stdin().read_line(&mut String::new())?; io::stdin().read_line(&mut String::new())?;
} }
} }
} }
let file = File::open(target_path)?; let app_ctx;
let app_ctx: AppContext = AppContext { file: &file, output_dir: &output_path_str };
if target_path.is_file() {
let file = File::open(&target_path)?;
app_ctx = AppContext {
input: InputTarget::File(file),
output_dir: output_directory_path,
};
} else if target_path.is_dir() {
app_ctx = AppContext {
input: InputTarget::Directory(target_path),
output_dir: output_directory_path,
};
} else {
return Err("Invalid input path!".into());
}
let formats: Vec<Format> = get_registry(); let formats: Vec<Format> = get_registry();
println!("Loaded {} formats!\n", formats.len());
for format in formats { for format in formats {
if let Some(ctx) = (format.detector_func)(&app_ctx)? { if let Some(ctx) = (format.detector_func)(&app_ctx)? {
println!("{} detected!", format.name); println!("{} detected!", format.name);