TPV TIMG extractor added

This commit is contained in:
theubusu
2025-09-25 17:49:02 +02:00
parent ee534675d8
commit 81fd148cb8
7 changed files with 163 additions and 3 deletions
Generated
+35
View File
@@ -2,6 +2,12 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 4 version = 4
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]] [[package]]
name = "aes" name = "aes"
version = "0.8.4" version = "0.8.4"
@@ -192,6 +198,15 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "crc32fast"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "crypto-common" name = "crypto-common"
version = "0.1.6" version = "0.1.6"
@@ -218,6 +233,16 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d"
[[package]]
name = "flate2"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]] [[package]]
name = "generic-array" name = "generic-array"
version = "0.14.7" version = "0.14.7"
@@ -291,6 +316,15 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]] [[package]]
name = "once_cell_polyfill" name = "once_cell_polyfill"
version = "1.70.1" version = "1.70.1"
@@ -368,6 +402,7 @@ dependencies = [
"aes", "aes",
"cbc", "cbc",
"clap", "clap",
"flate2",
"lz4", "lz4",
"lzma-rs", "lzma-rs",
"md5", "md5",
+2 -1
View File
@@ -10,4 +10,5 @@ lzma-rs = "0.3"
aes = "0.8.4" aes = "0.8.4"
cbc = "0.1.2" cbc = "0.1.2"
md5 = "0.7" md5 = "0.7"
sha1 = "0.10" sha1 = "0.10"
flate2 = "1.0"
+2
View File
@@ -6,5 +6,7 @@ pub fn read_file(mut file: &File, offset: u64, size: usize) -> Result<Vec<u8>, B
let mut buffer = vec![0u8; size]; let mut buffer = vec![0u8; size];
let _bytes_read = file.read(&mut buffer)?; let _bytes_read = file.read(&mut buffer)?;
// reset seek (!
file.seek(SeekFrom::Start(offset))?;
Ok(buffer) Ok(buffer)
} }
+2 -1
View File
@@ -1,2 +1,3 @@
pub mod mstar; pub mod mstar;
pub mod samsung_old; pub mod samsung_old;
pub mod tpv_timg;
+2
View File
@@ -147,6 +147,8 @@ pub fn extract_samsung_old(path: &PathBuf, output_folder: &str) -> Result<(), Bo
} }
} }
println!();
println!("Extraction finished!");
Ok(()) Ok(())
} }
+115
View File
@@ -0,0 +1,115 @@
use std::str;
use std::path::{Path};
use std::io::{self, Read, Write};
use std::fs::{self, File, OpenOptions};
use flate2::read::GzDecoder;
use crate::common;
pub fn is_tpv_timg_file(file: &File) -> bool {
let header = common::read_file(&file, 0, 4).expect("Failed to read from file.");
let header_string = String::from_utf8_lossy(&header);
if header_string == "TIMG"{
true
} else {
false
}
}
fn decompress_gzip(compressed_data: &[u8]) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let mut decoder = GzDecoder::new(compressed_data);
let mut decompressed = Vec::new();
decoder.read_to_end(&mut decompressed)?;
Ok(decompressed)
}
fn read_exact<R: Read>(reader: &mut R, size: usize) -> io::Result<Vec<u8>> {
let mut buf = vec![0u8; size];
reader.read_exact(&mut buf)?;
Ok(buf)
}
fn string_from_bytes(buf: &[u8]) -> String {
let end = buf.iter().position(|&b| b == 0).unwrap_or(buf.len());
String::from_utf8_lossy(&buf[..end]).to_string()
}
pub fn extract_tpv_timg(mut file: &File, output_folder: &str) -> Result<(), Box<dyn std::error::Error>> {
//TIMG header
let _timg = read_exact(&mut file, 288)?;
loop {
//PIMG
let mut pimg = [0u8; 4];
if file.read_exact(&mut pimg).is_err() {
break; //EOF
} else {
assert!(&pimg == b"PIMG", "Invalid PIMG section!");
}
//4 bytes 00
let _ = read_exact(&mut file, 4)?;
//4 bytes size
let size_bytes = read_exact(&mut file, 4)?;
let size = u32::from_le_bytes(size_bytes.try_into().unwrap());
//4 bytes nothing
let _ = read_exact(&mut file, 4)?;
//16 bytes checksum? or maybe signature
let _checksum = read_exact(&mut file, 16)?;
//16 bytes name
let name_bytes = read_exact(&mut file, 16)?;
let name = string_from_bytes(&name_bytes);
//64 bytes destination device
let dev_bytes = read_exact(&mut file, 64)?;
let dev = string_from_bytes(&dev_bytes);
//16 bytes compression type
let comp_bytes = read_exact(&mut file, 16)?;
let comp_type = string_from_bytes(&comp_bytes);
//1032 bytes maybe comment? skip this
let _ = read_exact(&mut file, 1032)?;
//actual data
let data = read_exact(&mut file, size as usize)?;
println!("PIMG - Name: {} Size: {} Dest: {} Compression: {}", name, size, dev, comp_type);
let out_data;
if comp_type == "gzip" {
println!("- Decompressing gzip...");
out_data = decompress_gzip(&data)?;
} else if comp_type == "none" {
out_data = data;
} else {
println!("- Warning: unsupported compression type!");
out_data = data;
}
let output_path = Path::new(&output_folder).join(name + ".bin");
fs::create_dir_all(&output_folder)?;
let mut out_file = OpenOptions::new()
.append(true)
.create(true)
.open(output_path)?;
out_file.write_all(&out_data)?;
println!("- Saved file!");
}
println!();
println!("Extraction finished!");
Ok(())
}
+5 -1
View File
@@ -34,7 +34,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
println!(); println!();
if formats::mstar::is_mstar_file(&file) { if formats::tpv_timg::is_tpv_timg_file(&file) {
println!("TPV TIMG file detected!");
println!();
formats::tpv_timg::extract_tpv_timg(&file, &output_path)?;
} else if formats::mstar::is_mstar_file(&file) {
println!("Mstar upgrade file detected!"); println!("Mstar upgrade file detected!");
println!(); println!();
formats::mstar::extract_mstar(&file, &output_path)?; formats::mstar::extract_mstar(&file, &output_path)?;