diff --git a/Cargo.lock b/Cargo.lock index c05c760..6c009d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cc" version = "1.2.36" @@ -108,6 +114,21 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "find-msvc-tools" version = "0.1.1" @@ -151,6 +172,16 @@ dependencies = [ "libc", ] +[[package]] +name = "lzma-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" +dependencies = [ + "byteorder", + "crc", +] + [[package]] name = "once_cell_polyfill" version = "1.70.1" @@ -210,6 +241,7 @@ version = "0.1.0" dependencies = [ "clap", "lz4", + "lzma-rs", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 991eb16..1a85951 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,4 +5,5 @@ edition = "2024" [dependencies] clap = { version = "4.5", features = ["derive"] } -lz4 = "1.23.1" \ No newline at end of file +lz4 = "1.23.1" +lzma-rs = "0.3" \ No newline at end of file diff --git a/src/formats/mstar.rs b/src/formats/mstar.rs index e3abfeb..4fa9110 100644 --- a/src/formats/mstar.rs +++ b/src/formats/mstar.rs @@ -1,14 +1,15 @@ use std::fs::{self, File, OpenOptions}; use std::path::{Path}; -use std::io::{Write}; +use std::io::{Write, Cursor}; use lz4::block::decompress; +use lzma_rs::lzma_decompress; use crate::common; pub fn is_mstar_file(file: &File) -> bool { let header = common::read_file(&file, 0, 32768).expect("Failed to read from file."); let header_string = String::from_utf8_lossy(&header); - if header_string.contains("filepartload") & header_string.contains("MstarUpgrade") | header_string.contains("CtvUpgrade") { + if header_string.contains("filepartload") & header_string.contains("MstarUpgrade") | header_string.contains("CtvUpgrade") | header_string.contains("LetvUpgrade"){ true } else { false @@ -23,6 +24,14 @@ fn parse_number(s: &str) -> Option { } } +fn decompress_lzma(compressed_data: &[u8]) -> Result, Box> { + let mut input = Cursor::new(compressed_data); + let mut output = Vec::new(); + + lzma_decompress(&mut input, &mut output)?; + Ok(output) +} + fn decompress_lz4(compressed_data: &[u8], original_size: i32) -> Result, std::io::Error> { match decompress(compressed_data, Some(original_size)) { Ok(decompressed) => Ok(decompressed), @@ -32,6 +41,7 @@ fn decompress_lz4(compressed_data: &[u8], original_size: i32) -> Result, pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box> { println!(); + let mut script = common::read_file(&file, 0, 32768)?; if let Some(pos) = script.iter().position(|x| [0x00, 0xFF].contains(x)) { @@ -40,11 +50,15 @@ pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box Result<(), Box = lines[j].split_whitespace().collect(); lz4_expect_size = parse_number(parts[5]).unwrap_or(0); } + if lines[j].starts_with("mmc unlzo"){ + compression = "lzo"; + let parts: Vec<&str> = lines[j].split_whitespace().collect(); + // get part name from mmc unlzo + if partname == "unknown" { + partname = parts[4] + } + } // check if its boot partition if lines[j].starts_with("mmc write.boot") { @@ -106,40 +131,51 @@ pub fn extract_mstar(file: &File, output_folder: &str) -> Result<(), Box {} (compression: {})", offset, size, partname, compression); + println!("Part - Offset: {}, Size: {} --> {}.bin", offset, size, partname); - let data = common::read_file(&file, offset, size.try_into().unwrap())?; - - //println!("{:02x?}", &data[0]); - //println!("{}", data.len()); - - let out_data; - - if compression == "lzma" { - // TODO: do lzma decompression - out_data = data; - } else if compression == "lz4" { - println!("- Decompressing lz4, expected size: {}", lz4_expect_size); - out_data = decompress_lz4(&data, lz4_expect_size.try_into().unwrap())?; - //out_data = data; + if partname == "unknown"{ + println!("- Unknown destination, skipping!"); } else { - out_data = data; - } + let data = common::read_file(&file, offset, size.try_into().unwrap())?; + let out_data; - let output_path = Path::new(&output_folder).join(partname.to_owned() + ".bin"); + if compression == "lzma" { + println!("- Decompressing LZMA..."); + out_data = decompress_lzma(&data)?; + } else if compression == "double_lzma" { + println!("- Decompressing LZMA (Pass 1)..."); + let pass_1 = decompress_lzma(&data)?; + println!("- Decompressing LZMA (Pass 2)..."); + out_data = decompress_lzma(&pass_1)?; + } else if compression == "lz4" { + println!("- Decompressing lz4, expected size: {}", lz4_expect_size); + out_data = decompress_lz4(&data, lz4_expect_size.try_into().unwrap())?; + } else if compression == "lzo" { + //TODO: add lzo + println!("- Decompressing lzo..."); + out_data = data; + }else { + out_data = data; + } - fs::create_dir_all(&output_folder)?; - let mut out_file = OpenOptions::new() - .append(true) - .create(true) - .open(output_path)?; + let output_path = Path::new(&output_folder).join(partname.to_owned() + ".bin"); - out_file.write_all(&out_data)?; + 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)?; + } } } i += 1; } + println!(); + println!("Extraction finished!"); + Ok(()) } \ No newline at end of file