diff options
Diffstat (limited to 'unpacker')
-rw-r--r-- | unpacker/Cargo.toml | 9 | ||||
-rw-r--r-- | unpacker/README.md | 0 | ||||
-rw-r--r-- | unpacker/src/main.rs | 124 |
3 files changed, 133 insertions, 0 deletions
diff --git a/unpacker/Cargo.toml b/unpacker/Cargo.toml new file mode 100644 index 0000000..adb64ec --- /dev/null +++ b/unpacker/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "unpacker" +version = "0.1.1" +edition = "2021" + +[dependencies] +byteorder = "1.4.3" +serde = { version = "1.0.160", features = ["derive"] } +serde_json = "1.0.96" diff --git a/unpacker/README.md b/unpacker/README.md new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/unpacker/README.md diff --git a/unpacker/src/main.rs b/unpacker/src/main.rs new file mode 100644 index 0000000..9d27385 --- /dev/null +++ b/unpacker/src/main.rs @@ -0,0 +1,124 @@ +use std::env; +use std::fs::File; +use std::io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write}; + +use byteorder::{ByteOrder, LittleEndian}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct FileHeader { + pub size: u32, + pub total: u32, + pub type1: u32, + pub type2: u32, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ListElement { + pub extension: String, + pub index: u32, + pub name: String, + #[serde(skip_serializing)] + pub position: u32, + #[serde(skip_serializing)] + pub size: u32, + pub unknown0: u32, + pub unknown1: u32, + pub unknown2: u32, +} + +fn main() { + let args: Vec<String> = env::args().collect(); + + let input = &args[1]; + let output = &args[2]; + + unpack(String::from(input), String::from(output)); +} + +fn unpack(input: String, output: String) { + let file = File::open(input).unwrap(); + let metadata = file.metadata().unwrap(); + + let mut reader = BufReader::new(file); + let mut list: Vec<ListElement> = Vec::new(); + + // Считываем заголовок файла + let mut header_buffer = [0u8; 16]; + reader.seek(SeekFrom::Start(0)).unwrap(); + reader.read_exact(&mut header_buffer).unwrap(); + + let file_header = FileHeader { + size: LittleEndian::read_u32(&header_buffer[12..16]), + total: LittleEndian::read_u32(&header_buffer[8..12]), + type1: LittleEndian::read_u32(&header_buffer[0..4]), + type2: LittleEndian::read_u32(&header_buffer[4..8]), + }; + + if file_header.type1 != 1936020046 || file_header.type2 != 256 { + panic!("this isn't NRes file"); + } + + if metadata.len() != file_header.size as u64 { + panic!("incorrect size") + } + + // Считываем список файлов + let list_files_start_position = file_header.size - (file_header.total * 64); + let list_files_size = file_header.total * 64; + + let mut list_buffer = vec![0u8; list_files_size as usize]; + reader + .seek(SeekFrom::Start(list_files_start_position as u64)) + .unwrap(); + reader.read_exact(&mut list_buffer).unwrap(); + + if list_buffer.len() % 64 != 0 { + panic!("invalid files list") + } + + for i in 0..(list_buffer.len() / 64) { + let from = i * 64; + let to = (i * 64) + 64; + let chunk: &[u8] = &list_buffer[from..to]; + + let element_list = ListElement { + extension: String::from_utf8_lossy(&chunk[0..4]) + .trim_matches(char::from(0)) + .to_string(), + index: LittleEndian::read_u32(&chunk[60..64]), + name: String::from_utf8_lossy(&chunk[20..56]) + .trim_matches(char::from(0)) + .to_string(), + position: LittleEndian::read_u32(&chunk[56..60]), + size: LittleEndian::read_u32(&chunk[12..16]), + unknown0: LittleEndian::read_u32(&chunk[4..8]), + unknown1: LittleEndian::read_u32(&chunk[8..12]), + unknown2: LittleEndian::read_u32(&chunk[16..20]), + }; + + list.push(element_list) + } + + // Распаковываем файлы в директорию + for element in &list { + let path = format!("{}/{}", output, element.name); + let mut file = File::create(path).unwrap(); + + let mut file_buffer = vec![0u8; element.size as usize]; + reader + .seek(SeekFrom::Start(element.position as u64)) + .unwrap(); + reader.read_exact(&mut file_buffer).unwrap(); + + file.write_all(&file_buffer).unwrap(); + file_buffer.clear(); + } + + // Выгрузка списка файлов в JSON + let path = format!("{}/{}", output, "index.json"); + let file = File::create(path).unwrap(); + let mut writer = BufWriter::new(file); + serde_json::to_writer_pretty(&mut writer, &list).unwrap(); + writer.flush().unwrap(); +} |