mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-15 21:00:03 +02:00
add cd5 extractor
This commit is contained in:
@@ -36,6 +36,10 @@ Options:
|
||||
**Used in:** Enterprise HP Printers
|
||||
**Notes:** None, all files should be supported
|
||||
|
||||
## CD5
|
||||
**Used in:** Some Samsung TV tuners, and possibly other Irdeto(?)-based tuners
|
||||
**Notes:** Decryption is not supported.
|
||||
|
||||
## EPK v1
|
||||
**Used in:** LG TVs before ~2010
|
||||
**Notes:** None, all files should be supported
|
||||
|
||||
@@ -28,6 +28,7 @@ pub mod bdl;
|
||||
pub mod amlogic;
|
||||
pub mod sdboot;
|
||||
pub mod sdimage;
|
||||
pub mod cd5;
|
||||
|
||||
pub mod pup;
|
||||
|
||||
@@ -214,6 +215,11 @@ pub fn get_registry() -> Vec<Format> {
|
||||
detector_func: crate::formats::mtk_bdp::is_mtk_bdp_file,
|
||||
extractor_func: crate::formats::mtk_bdp::extract_mtk_bdp,
|
||||
},
|
||||
Format {
|
||||
name: "cd5",
|
||||
detector_func: crate::formats::cd5::is_cd5_file,
|
||||
extractor_func: crate::formats::cd5::extract_cd5,
|
||||
},
|
||||
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
use binrw::BinRead;
|
||||
|
||||
//all big endian
|
||||
|
||||
#[derive(BinRead)]
|
||||
pub struct DescriptorHeader {
|
||||
pub magic: u8,
|
||||
pub size: u16,
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
pub struct DownloadHeader {
|
||||
// preceeded by DescriptorHeader
|
||||
pub manufacturer_code: u16,
|
||||
pub hardware_version: u16,
|
||||
_unk1: u16,
|
||||
_unk2: u16,
|
||||
pub variant: u16,
|
||||
pub sub_variant: u16,
|
||||
_unk_string_bytes: [u8; 8],
|
||||
pub version: u16, // DSN/download sequence number
|
||||
pub module_count: u16,
|
||||
#[br(count=module_count)] pub module_entries: Vec<DownloadHeaderModuleEntry>,
|
||||
_signature_size: u16,
|
||||
#[br(count=_signature_size)] pub _signature: Vec<u8>,
|
||||
_extra_data_size: u16,
|
||||
#[br(count=_extra_data_size)] pub _extra_data: Vec<u8>,
|
||||
_checksum: u16, //crc16 of header (not including magic and size)
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
pub struct DownloadHeaderModuleEntry {
|
||||
pub module_id: u16,
|
||||
pub version: u16 //DSN
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
pub struct ModuleDownloadHeader {
|
||||
// preceeded by DescriptorHeader
|
||||
pub module_id: u16,
|
||||
flags: u8,
|
||||
pub out_size: u32,
|
||||
pub segment_size: u16,
|
||||
pub segment_count: u16,
|
||||
_checksum: u16, //crc16 of header (not including magic and size)
|
||||
}
|
||||
impl ModuleDownloadHeader {
|
||||
pub fn is_encrypted(&self) -> bool {
|
||||
(self.flags & (1 << 5)) != 0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
pub struct DownloadSegment {
|
||||
pub magic: u8,
|
||||
pub module_id: u16,
|
||||
pub data_size: u16,
|
||||
#[br(count=data_size)] pub data: Vec<u8>,
|
||||
_checksum: u16, //crc16 of module_id+size+(STORED)data
|
||||
}
|
||||
|
||||
#[derive(BinRead)]
|
||||
pub struct InnerModuleHeader {
|
||||
_module_id: u16,
|
||||
pub header_size: u16,
|
||||
pub data_size: u32,
|
||||
_unk1: u8,
|
||||
_version: u16, //dsn
|
||||
_variant: u16,
|
||||
_sub_variant: u16,
|
||||
_version2: u16,
|
||||
_unk2: [u8; 5],
|
||||
_signature1_size: u16,
|
||||
#[br(count=_signature1_size)] pub _signature1: Vec<u8>,
|
||||
_signature2_size: u16,
|
||||
#[br(count=_signature2_size)] pub _signature2: Vec<u8>,
|
||||
_checksum: u32, //crc32 of header
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
mod include;
|
||||
use std::any::Any;
|
||||
use crate::AppContext;
|
||||
|
||||
use std::path::Path;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{Cursor, Seek, SeekFrom, Write};
|
||||
use binrw::BinReaderExt;
|
||||
|
||||
use crate::utils::common;
|
||||
use include::*;
|
||||
|
||||
pub fn is_cd5_file(app_ctx: &AppContext) -> Result<Option<Box<dyn Any>>, Box<dyn std::error::Error>> {
|
||||
let file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
|
||||
|
||||
let hdr_magic = common::read_file(&file, 15, 8)?;
|
||||
if hdr_magic == b"20 10001" { //not sure about it but fine for samsung and telestar
|
||||
Ok(Some(Box::new(())))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_cd5(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut file = app_ctx.file().ok_or("Extractor expected file")?;
|
||||
|
||||
let dwld_hdr_desc: DescriptorHeader = file.read_be()?;
|
||||
if dwld_hdr_desc.magic != 0x11 {
|
||||
return Err("Invalid download header magic!".into());
|
||||
}
|
||||
let mut dwld_hdr_reader = Cursor::new(common::read_exact(&mut file, dwld_hdr_desc.size as usize)?);
|
||||
let dwld_hdr: DownloadHeader = dwld_hdr_reader.read_be()?;
|
||||
|
||||
// like Loader Data screen
|
||||
println!("File info -\nManufacturer code: {}\nHardware Version: {}\nVersion(DSN): {}(0x{:02x})\nVariant/Sub-variant: 0x{:02x}/0x{:02x}\nModule count: {}",
|
||||
dwld_hdr.manufacturer_code, dwld_hdr.hardware_version, dwld_hdr.version, dwld_hdr.version, dwld_hdr.variant, dwld_hdr.sub_variant, dwld_hdr.module_count);
|
||||
|
||||
for (i, module) in dwld_hdr.module_entries.iter().enumerate() {
|
||||
let mod_hdr_desc: DescriptorHeader = file.read_be()?;
|
||||
if mod_hdr_desc.magic != 0x22 {
|
||||
return Err("Invalid module download header magic!".into());
|
||||
}
|
||||
let mut mod_hdr_reader = Cursor::new(common::read_exact(&mut file, mod_hdr_desc.size as usize)?);
|
||||
let mod_hdr: ModuleDownloadHeader = mod_hdr_reader.read_be()?;
|
||||
if mod_hdr.module_id != module.module_id {
|
||||
return Err("Module id mismatch in download header and module header!".into());
|
||||
}
|
||||
|
||||
println!("\n({}/{}) Module {}(0x{:02x}) - Version(DSN): {}(0x{:02x}), Size: {}, Segment size: {}, Segment count: {} {}",
|
||||
i+1, dwld_hdr.module_count, mod_hdr.module_id, mod_hdr.module_id, module.version, module.version, mod_hdr.out_size, mod_hdr.segment_size, mod_hdr.segment_count,
|
||||
if mod_hdr.is_encrypted() {"[ENCRYPTED]"} else {""});
|
||||
|
||||
let mut module_data: Vec<u8> = Vec::new();
|
||||
|
||||
for s_i in 0..mod_hdr.segment_count {
|
||||
let mut segment: DownloadSegment = file.read_be()?;
|
||||
if segment.magic != 0x33 {
|
||||
return Err("Invalid segment magic!".into());
|
||||
}
|
||||
if segment.module_id != mod_hdr.module_id {
|
||||
return Err("Module id mismatch in segment and module header!".into());
|
||||
}
|
||||
|
||||
println!(" Segment {}/{} - Size: {}", s_i+1, mod_hdr.segment_count, segment.data_size);
|
||||
module_data.append(&mut segment.data);
|
||||
}
|
||||
|
||||
let out_data;
|
||||
if mod_hdr.is_encrypted() {
|
||||
println!("- Warning: data is encrypted, so cannot read inner header - saving ENCRYPTED data!");
|
||||
out_data = module_data;
|
||||
}
|
||||
else {
|
||||
let mut mod_data_rdr = Cursor::new(module_data);
|
||||
let inner_mod_hdr: InnerModuleHeader = mod_data_rdr.read_be()?;
|
||||
println!("- Inner header size: {}, Data size: {}", inner_mod_hdr.header_size, inner_mod_hdr.data_size);
|
||||
mod_data_rdr.seek(SeekFrom::Start(inner_mod_hdr.header_size as u64))?;
|
||||
out_data = common::read_exact(&mut mod_data_rdr, inner_mod_hdr.data_size as usize)?;
|
||||
}
|
||||
|
||||
let output_path = Path::new(&app_ctx.output_dir).join(format!("{}.bin", mod_hdr.module_id));
|
||||
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)?;
|
||||
|
||||
println!("-- Saved file!");
|
||||
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user