From 4e195ec38657605657dcbcc5b76581cbf5b8c404 Mon Sep 17 00:00:00 2001
From: Valentin Popov <valentin@popov.link>
Date: Sun, 17 Sep 2023 02:35:58 +0400
Subject: Перенос наработок в новый репозиторий
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 unpacker/src/main.rs | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)
 create mode 100644 unpacker/src/main.rs

(limited to 'unpacker/src/main.rs')

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();
+}
-- 
cgit v1.2.3