mirror of
https://github.com/Ap0dexMe0/unixtract.git
synced 2026-07-16 05:04:22 +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.
|
**Notes:** None, all files should be supported.
|
||||||
|
|
||||||
## Novatek TIMG
|
## Novatek TIMG
|
||||||
**Used in:** Newer Novatek-based TVs (Philips TitanOS, Hisense)
|
**Used in:** Newer Novatek-based TVs (Philips(TPVision), Hisense, TCL...)
|
||||||
**Notes:** There is an older type of this format that is not yet supported, but for newer type all files should work.
|
**Notes:** None, all files should be supported.
|
||||||
|
|
||||||
## Panasonic Blu-Ray (PANA_DVD.FRM, PANA_ESD.FRM, PANAEUSB.FRM)
|
## Panasonic Blu-Ray (PANA_DVD.FRM, PANA_ESD.FRM, PANAEUSB.FRM)
|
||||||
**Used in:** Panasonic Blu-Ray Players and Recorders
|
**Used in:** Panasonic Blu-Ray Players and Recorders
|
||||||
|
|||||||
+180
-19
@@ -1,38 +1,199 @@
|
|||||||
use crate::utils::common;
|
use crate::utils::common;
|
||||||
use binrw::BinRead;
|
use binrw::BinRead;
|
||||||
|
|
||||||
#[derive(Debug, BinRead)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
pub struct TIMG {
|
pub enum TimgVariant {
|
||||||
_magic_bytes: [u8; 4], //TIMG
|
Old,
|
||||||
_unused1: u32,
|
Old2,
|
||||||
pub data_size: u32,
|
New,
|
||||||
_unused2: u32,
|
}
|
||||||
_md5_checksum: [u8; 16],
|
|
||||||
_signature: [u8; 256],
|
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)]
|
#[derive(Debug, BinRead)]
|
||||||
pub struct PIMG {
|
pub struct TIMG64 { //new
|
||||||
pub magic_bytes: [u8; 4], //PIMG
|
_magic_bytes: [u8; 8], //TIMG/x00/x00/x00/x00
|
||||||
_unused1: u32,
|
data_size: u64,
|
||||||
pub size: u32,
|
_data_checksum: [u8; 16],
|
||||||
_unused2: u32,
|
_signature: [u8; 256],
|
||||||
_md5_checksum: [u8; 16],
|
}
|
||||||
|
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],
|
name_bytes: [u8; 16],
|
||||||
dest_dev_bytes: [u8; 64],
|
dest_dev_bytes: [u8; 64],
|
||||||
comp_type_bytes: [u8; 16],
|
comp_type_bytes: [u8; 16],
|
||||||
_unknown1: u32,
|
_unknown1: u32,
|
||||||
_comment: [u8; 1024],
|
comment_bytes: [u8; 1024],
|
||||||
_unknown2: u32,
|
_unknown2: u32,
|
||||||
}
|
}
|
||||||
impl PIMG {
|
impl PIMG for PIMG64 {
|
||||||
pub fn name(&self) -> String {
|
fn magic_bytes(&self) -> Vec<u8> {
|
||||||
|
self.magic_bytes.to_vec()
|
||||||
|
}
|
||||||
|
fn name(&self) -> String {
|
||||||
common::string_from_bytes(&self.name_bytes)
|
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)
|
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)
|
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 crate::utils::sparse::{unsparse_to_file};
|
||||||
use include::*;
|
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>> {
|
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 file = match app_ctx.file() {Some(f) => f, None => return Ok(None)};
|
||||||
|
|
||||||
let header = common::read_file(&file, 0, 4)?;
|
let header = common::read_file(&file, 0, 8)?;
|
||||||
if header == b"TIMG" {
|
if header == b"TIMG\x00\x00\x00\x00" { //new variant checks magic as 64bit int (probably)
|
||||||
Ok(Some(Box::new(())))
|
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 {
|
} else {
|
||||||
Ok(None)
|
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 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: Box<dyn TIMG> = match ctx.variant {
|
||||||
let timg: TIMG = file.read_le()?;
|
TimgVariant::New => Box::new(file.read_le::<TIMG64>()?),
|
||||||
println!("File info:\nData size: {}", timg.data_size);
|
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;
|
let mut pimg_i = 0;
|
||||||
while file.stream_position()? < file_size as u64 {
|
while file.stream_position()? < end {
|
||||||
pimg_i += 1;
|
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());
|
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 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");
|
||||||
@@ -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
|
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...");
|
||||||
out_data = decompress_gzip(&data)?;
|
out_data = decompress_gzip(&data)?;
|
||||||
|
|
||||||
} else if pimg.comp_type() == "none" || pimg.comp_type() == "" {
|
} else if pimg.comp_type() == "none" || pimg.comp_type() == "" {
|
||||||
out_data = data;
|
out_data = data;
|
||||||
|
|
||||||
} else if pimg.comp_type() == "sparse" {
|
} else if pimg.comp_type() == "sparse" {
|
||||||
println!("- Unsparsing...");
|
println!("- Unsparsing...");
|
||||||
unsparse_to_file(&data, output_path)?;
|
unsparse_to_file(&data, output_path)?;
|
||||||
println!("-- Saved file!");
|
println!("-- Saved file!");
|
||||||
continue
|
continue
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
println!("- Warning: unsupported compression type, saving stored data!");
|
println!("- Warning: unsupported compression type, saving stored data!");
|
||||||
out_data = data;
|
out_data = data;
|
||||||
|
|||||||
Reference in New Issue
Block a user