mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-15 21:00:03 +02:00
nvt_timg: add support for more variants/types
This commit is contained in:
@@ -129,8 +129,8 @@ Options:
|
||||
**Notes:** None, all files should be supported.
|
||||
|
||||
## Novatek TIMG
|
||||
**Used in:** Newer Novatek-based TVs (Philips TitanOS, Hisense)
|
||||
**Notes:** There is an older type of this format that is not yet supported, but for newer type all files should work.
|
||||
**Used in:** Newer Novatek-based TVs (Philips(TPVision), Hisense, TCL...)
|
||||
**Notes:** None, all files should be supported.
|
||||
|
||||
## Panasonic Blu-Ray (PANA_DVD.FRM, PANA_ESD.FRM, PANAEUSB.FRM)
|
||||
**Used in:** Panasonic Blu-Ray Players and Recorders
|
||||
|
||||
+180
-19
@@ -1,38 +1,199 @@
|
||||
use crate::utils::common;
|
||||
use binrw::BinRead;
|
||||
|
||||
#[derive(Debug, BinRead)]
|
||||
pub struct TIMG {
|
||||
_magic_bytes: [u8; 4], //TIMG
|
||||
_unused1: u32,
|
||||
pub data_size: u32,
|
||||
_unused2: u32,
|
||||
_md5_checksum: [u8; 16],
|
||||
_signature: [u8; 256],
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub enum TimgVariant {
|
||||
Old,
|
||||
Old2,
|
||||
New,
|
||||
}
|
||||
|
||||
pub trait TIMG {
|
||||
fn _magic_bytes(&self) -> Vec<u8>;
|
||||
fn data_size(&self) -> usize;
|
||||
fn _data_checksum(&self) -> [u8; 16]; //md5 of data_size after timg header
|
||||
fn _signature(&self) -> [u8; 256];
|
||||
}
|
||||
|
||||
#[derive(Debug, BinRead)]
|
||||
pub struct PIMG {
|
||||
pub magic_bytes: [u8; 4], //PIMG
|
||||
_unused1: u32,
|
||||
pub size: u32,
|
||||
_unused2: u32,
|
||||
_md5_checksum: [u8; 16],
|
||||
pub struct TIMG64 { //new
|
||||
_magic_bytes: [u8; 8], //TIMG/x00/x00/x00/x00
|
||||
data_size: u64,
|
||||
_data_checksum: [u8; 16],
|
||||
_signature: [u8; 256],
|
||||
}
|
||||
impl TIMG for TIMG64 {
|
||||
fn _magic_bytes(&self) -> Vec<u8> {
|
||||
self._magic_bytes.to_vec()
|
||||
}
|
||||
fn data_size(&self) -> usize {
|
||||
self.data_size as usize
|
||||
}
|
||||
fn _data_checksum(&self) -> [u8; 16] {
|
||||
self._data_checksum
|
||||
}
|
||||
fn _signature(&self) -> [u8; 256] {
|
||||
self._signature
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, BinRead)]
|
||||
pub struct TIMG32 {
|
||||
_magic_bytes: [u8; 4], //TIMG
|
||||
data_size: u32,
|
||||
_data_checksum: [u8; 16],
|
||||
_signature: [u8; 256],
|
||||
}
|
||||
impl TIMG for TIMG32 {
|
||||
fn _magic_bytes(&self) -> Vec<u8> {
|
||||
self._magic_bytes.to_vec()
|
||||
}
|
||||
fn data_size(&self) -> usize {
|
||||
self.data_size as usize
|
||||
}
|
||||
fn _data_checksum(&self) -> [u8; 16] {
|
||||
self._data_checksum
|
||||
}
|
||||
fn _signature(&self) -> [u8; 256] {
|
||||
self._signature
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, BinRead)]
|
||||
pub struct TIMGOld2 {
|
||||
_magic_bytes: [u8; 4], //TIMG
|
||||
data_size: u32,
|
||||
_data_checksum: [u8; 16],
|
||||
_pad: u32,
|
||||
_signature: [u8; 256],
|
||||
}
|
||||
impl TIMG for TIMGOld2 {
|
||||
fn _magic_bytes(&self) -> Vec<u8> {
|
||||
self._magic_bytes.to_vec()
|
||||
}
|
||||
fn data_size(&self) -> usize {
|
||||
self.data_size as usize
|
||||
}
|
||||
fn _data_checksum(&self) -> [u8; 16] {
|
||||
self._data_checksum
|
||||
}
|
||||
fn _signature(&self) -> [u8; 256] {
|
||||
self._signature
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PIMG {
|
||||
fn magic_bytes(&self) -> Vec<u8>;
|
||||
fn name(&self) -> String;
|
||||
fn size(&self) -> usize;
|
||||
fn _checksum(&self) -> [u8; 16]; //md5 of stored data
|
||||
fn dest_dev(&self) -> String;
|
||||
fn comp_type(&self) -> String;
|
||||
fn comment(&self) -> String;
|
||||
}
|
||||
|
||||
#[derive(Debug, BinRead)]
|
||||
pub struct PIMG64 {
|
||||
magic_bytes: [u8; 8], //PIMG\x00\x00\x00\x00
|
||||
size: u64,
|
||||
_checksum: [u8; 16],
|
||||
name_bytes: [u8; 16],
|
||||
dest_dev_bytes: [u8; 64],
|
||||
comp_type_bytes: [u8; 16],
|
||||
_unknown1: u32,
|
||||
_comment: [u8; 1024],
|
||||
comment_bytes: [u8; 1024],
|
||||
_unknown2: u32,
|
||||
}
|
||||
impl PIMG {
|
||||
pub fn name(&self) -> String {
|
||||
impl PIMG for PIMG64 {
|
||||
fn magic_bytes(&self) -> Vec<u8> {
|
||||
self.magic_bytes.to_vec()
|
||||
}
|
||||
fn name(&self) -> String {
|
||||
common::string_from_bytes(&self.name_bytes)
|
||||
}
|
||||
pub fn dest_dev(&self) -> String {
|
||||
fn size(&self) -> usize {
|
||||
self.size as usize
|
||||
}
|
||||
fn _checksum(&self) -> [u8; 16] {
|
||||
self._checksum
|
||||
}
|
||||
fn dest_dev(&self) -> String {
|
||||
common::string_from_bytes(&self.dest_dev_bytes)
|
||||
}
|
||||
pub fn comp_type(&self) -> String {
|
||||
fn comp_type(&self) -> String {
|
||||
common::string_from_bytes(&self.comp_type_bytes)
|
||||
}
|
||||
fn comment(&self) -> String {
|
||||
common::string_from_bytes(&self.comment_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, BinRead)]
|
||||
pub struct PIMG32 {
|
||||
magic_bytes: [u8; 4], //PIMG
|
||||
size: u32,
|
||||
_checksum: [u8; 16],
|
||||
name_bytes: [u8; 16],
|
||||
dest_dev_bytes: [u8; 32],
|
||||
comp_type_bytes: [u8; 16],
|
||||
_unknown1: u32,
|
||||
comment_bytes: [u8; 1024],
|
||||
_unknown2: u32,
|
||||
}
|
||||
impl PIMG for PIMG32 {
|
||||
fn magic_bytes(&self) -> Vec<u8> {
|
||||
self.magic_bytes.to_vec()
|
||||
}
|
||||
fn name(&self) -> String {
|
||||
common::string_from_bytes(&self.name_bytes)
|
||||
}
|
||||
fn size(&self) -> usize {
|
||||
self.size as usize
|
||||
}
|
||||
fn _checksum(&self) -> [u8; 16] {
|
||||
self._checksum
|
||||
}
|
||||
fn dest_dev(&self) -> String {
|
||||
common::string_from_bytes(&self.dest_dev_bytes)
|
||||
}
|
||||
fn comp_type(&self) -> String {
|
||||
common::string_from_bytes(&self.comp_type_bytes)
|
||||
}
|
||||
fn comment(&self) -> String {
|
||||
common::string_from_bytes(&self.comment_bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, BinRead)]
|
||||
pub struct PIMGOld2 {
|
||||
magic_bytes: [u8; 4], //PIMG
|
||||
size: u32,
|
||||
_checksum: [u8; 16],
|
||||
name_bytes: [u8; 16],
|
||||
dest_dev_bytes: [u8; 24],
|
||||
comp_type_bytes: [u8; 16],
|
||||
_unknown1: u32,
|
||||
}
|
||||
impl PIMG for PIMGOld2 {
|
||||
fn magic_bytes(&self) -> Vec<u8> {
|
||||
self.magic_bytes.to_vec()
|
||||
}
|
||||
fn name(&self) -> String {
|
||||
common::string_from_bytes(&self.name_bytes)
|
||||
}
|
||||
fn size(&self) -> usize {
|
||||
self.size as usize
|
||||
}
|
||||
fn _checksum(&self) -> [u8; 16] {
|
||||
self._checksum
|
||||
}
|
||||
fn dest_dev(&self) -> String {
|
||||
common::string_from_bytes(&self.dest_dev_bytes)
|
||||
}
|
||||
fn comp_type(&self) -> String {
|
||||
common::string_from_bytes(&self.comp_type_bytes)
|
||||
}
|
||||
fn comment(&self) -> String {
|
||||
"".to_string() //yes (this variant has no comment)
|
||||
}
|
||||
}
|
||||
+46
-12
@@ -12,35 +12,66 @@ use crate::utils::compression::{decompress_gzip};
|
||||
use crate::utils::sparse::{unsparse_to_file};
|
||||
use include::*;
|
||||
|
||||
pub struct TimgContext {
|
||||
variant: TimgVariant,
|
||||
}
|
||||
|
||||
pub fn is_nvt_timg_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 header = common::read_file(&file, 0, 4)?;
|
||||
if header == b"TIMG" {
|
||||
Ok(Some(Box::new(())))
|
||||
let header = common::read_file(&file, 0, 8)?;
|
||||
if header == b"TIMG\x00\x00\x00\x00" { //new variant checks magic as 64bit int (probably)
|
||||
Ok(Some(Box::new(TimgContext {variant: TimgVariant::New})))
|
||||
|
||||
} else if header.starts_with(b"TIMG") {
|
||||
//check based on where the first PIMG appears, since Old2 header is 4 bytes bigger, it will appear later
|
||||
let check = common::read_file(&file, 280, 8)?;
|
||||
if &check[0..4] == b"PIMG" {
|
||||
Ok(Some(Box::new(TimgContext {variant: TimgVariant::Old})))
|
||||
}
|
||||
else if &check[4..8] == b"PIMG" {
|
||||
Ok(Some(Box::new(TimgContext {variant: TimgVariant::Old2})))
|
||||
}
|
||||
else {
|
||||
Ok(None) //?
|
||||
}
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn extract_nvt_timg(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 ctx = ctx.downcast::<TimgContext>().expect("Missing context");
|
||||
|
||||
let file_size = file.metadata()?.len();
|
||||
let timg: TIMG = file.read_le()?;
|
||||
println!("File info:\nData size: {}", timg.data_size);
|
||||
let timg: Box<dyn TIMG> = match ctx.variant {
|
||||
TimgVariant::New => Box::new(file.read_le::<TIMG64>()?),
|
||||
TimgVariant::Old => Box::new(file.read_le::<TIMG32>()?),
|
||||
TimgVariant::Old2 => Box::new(file.read_le::<TIMGOld2>()?),
|
||||
};
|
||||
println!("File info:\nVariant: {:?}\nData size: {}", ctx.variant, timg.data_size());
|
||||
|
||||
//position after header + data size
|
||||
let end = file.stream_position()? + timg.data_size() as u64;
|
||||
|
||||
let mut pimg_i = 0;
|
||||
while file.stream_position()? < file_size as u64 {
|
||||
while file.stream_position()? < end {
|
||||
pimg_i += 1;
|
||||
let pimg: PIMG = file.read_le()?;
|
||||
if &pimg.magic_bytes != b"PIMG" {
|
||||
|
||||
let pimg: Box<dyn PIMG> = match ctx.variant {
|
||||
TimgVariant::New => Box::new(file.read_le::<PIMG64>()?),
|
||||
TimgVariant::Old => Box::new(file.read_le::<PIMG32>()?),
|
||||
TimgVariant::Old2 => Box::new(file.read_le::<PIMGOld2>()?),
|
||||
};
|
||||
|
||||
if !pimg.magic_bytes().starts_with(b"PIMG") {
|
||||
return Err("Invalid PIMG magic!".into());
|
||||
}
|
||||
|
||||
let data = common::read_exact(&mut file, pimg.size as usize)?;
|
||||
let data = common::read_exact(&mut file, pimg.size())?;
|
||||
|
||||
println!("\n#{} - {}, Size: {}, Dest: {}, Compression: {}", pimg_i, pimg.name(), pimg.size, pimg.dest_dev(), pimg.comp_type());
|
||||
println!("\n#{} - {}, Size: {}, Dest: {}, Compression: {}, Comment: {}",
|
||||
pimg_i, pimg.name(), pimg.size(), pimg.dest_dev(), pimg.comp_type(), pimg.comment());
|
||||
|
||||
let out_data;
|
||||
let output_path = Path::new(&app_ctx.output_dir).join(pimg.name() + ".bin");
|
||||
@@ -48,13 +79,16 @@ pub fn extract_nvt_timg(app_ctx: &AppContext, _ctx: Box<dyn Any>) -> Result<(),
|
||||
if pimg.comp_type() == "gzip" && data.starts_with(b"\x1F\x8B") { //additionally check for gzip header, because sometimes its deceptive
|
||||
println!("- Decompressing gzip...");
|
||||
out_data = decompress_gzip(&data)?;
|
||||
|
||||
} else if pimg.comp_type() == "none" || pimg.comp_type() == "" {
|
||||
out_data = data;
|
||||
|
||||
} else if pimg.comp_type() == "sparse" {
|
||||
println!("- Unsparsing...");
|
||||
unsparse_to_file(&data, output_path)?;
|
||||
println!("-- Saved file!");
|
||||
continue
|
||||
|
||||
} else {
|
||||
println!("- Warning: unsupported compression type, saving stored data!");
|
||||
out_data = data;
|
||||
|
||||
Reference in New Issue
Block a user