summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock105
-rw-r--r--Cargo.toml7
-rw-r--r--packer/Cargo.toml9
-rw-r--r--packer/README.md0
-rw-r--r--packer/src/main.rs170
-rw-r--r--unpacker/Cargo.toml9
-rw-r--r--unpacker/README.md0
-rw-r--r--unpacker/src/main.rs124
9 files changed, 425 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..5d240a1
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,105 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
+name = "itoa"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
+
+[[package]]
+name = "packer"
+version = "0.1.0"
+dependencies = [
+ "byteorder",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.67"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
+
+[[package]]
+name = "serde"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.188"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e02e55d62894af2a08aca894c6577281f76769ba47c94d5756bec8ac6e7373"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+
+[[package]]
+name = "unpacker"
+version = "0.1.1"
+dependencies = [
+ "byteorder",
+ "serde",
+ "serde_json",
+]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..3fc2b87
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,7 @@
+[workspace]
+members = ["packer", "unpacker"]
+
+[profile.release]
+codegen-units = 1
+lto = true
+strip = true
diff --git a/packer/Cargo.toml b/packer/Cargo.toml
new file mode 100644
index 0000000..cbf418c
--- /dev/null
+++ b/packer/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "packer"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+byteorder = "1.4.3"
+serde = { version = "1.0.160", features = ["derive"] }
+serde_json = "1.0.96"
diff --git a/packer/README.md b/packer/README.md
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/packer/README.md
diff --git a/packer/src/main.rs b/packer/src/main.rs
new file mode 100644
index 0000000..ae4e02a
--- /dev/null
+++ b/packer/src/main.rs
@@ -0,0 +1,170 @@
+use std::env;
+use std::{
+ fs::{self, File},
+ io::{BufReader, Read},
+};
+
+use byteorder::{ByteOrder, LittleEndian};
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize, Debug)]
+pub struct ImportListElement {
+ pub extension: String,
+ pub index: u32,
+ pub name: String,
+ pub unknown0: u32,
+ pub unknown1: u32,
+ pub unknown2: u32,
+}
+
+#[derive(Debug)]
+pub struct ListElement {
+ pub extension: String,
+ pub index: u32,
+ pub name: String,
+ pub position: u32,
+ 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];
+
+ pack(String::from(input), String::from(output));
+}
+
+fn pack(input: String, output: String) {
+ // Загружаем индекс-файл
+ let index_file = format!("{}/{}", input, "index.json");
+ let data = fs::read_to_string(index_file).unwrap();
+ let list: Vec<ImportListElement> = serde_json::from_str(&data).unwrap();
+
+ // Общий буфер хранения файлов
+ let mut content_buffer: Vec<u8> = Vec::new();
+ let mut list_buffer: Vec<u8> = Vec::new();
+
+ // Общее количество файлов
+ let total_files: u32 = list.len() as u32;
+
+ for (index, item) in list.iter().enumerate() {
+ // Открываем дескриптор файла
+ let path = format!("{}/{}", input, item.name);
+ let file = File::open(path).unwrap();
+ let metadata = file.metadata().unwrap();
+
+ // Считываем файл в буфер
+ let mut reader = BufReader::new(file);
+ let mut file_buffer: Vec<u8> = Vec::new();
+ reader.read_to_end(&mut file_buffer).unwrap();
+
+ // Выравнивание буфера
+ if index != 0 {
+ while content_buffer.len() % 8 != 0 {
+ content_buffer.push(0);
+ }
+ }
+
+ // Получение позиции файла
+ let position = content_buffer.len() + 16;
+
+ // Записываем файл в буфер
+ content_buffer.extend(file_buffer);
+
+ // Формируем элемент
+ let element = ListElement {
+ extension: item.extension.to_string(),
+ index: item.index,
+ name: item.name.to_string(),
+ position: position as u32,
+ size: metadata.len() as u32,
+ unknown0: item.unknown0,
+ unknown1: item.unknown1,
+ unknown2: item.unknown2,
+ };
+
+ // Создаем буфер из элемента
+ let mut element_buffer: Vec<u8> = Vec::new();
+
+ // Пишем тип файла
+ let mut extension_buffer: [u8; 4] = [0; 4];
+ let mut file_extension_buffer = element.extension.into_bytes();
+ file_extension_buffer.resize(4, 0);
+ extension_buffer.copy_from_slice(&file_extension_buffer);
+ element_buffer.extend(extension_buffer);
+
+ // Пишем неизвестное значение #1
+ let mut unknown0_buffer: [u8; 4] = [0; 4];
+ LittleEndian::write_u32(&mut unknown0_buffer, element.unknown0);
+ element_buffer.extend(unknown0_buffer);
+
+ // Пишем неизвестное значение #2
+ let mut unknown1_buffer: [u8; 4] = [0; 4];
+ LittleEndian::write_u32(&mut unknown1_buffer, element.unknown1);
+ element_buffer.extend(unknown1_buffer);
+
+ // Пишем размер файла
+ let mut file_size_buffer: [u8; 4] = [0; 4];
+ LittleEndian::write_u32(&mut file_size_buffer, element.size);
+ element_buffer.extend(file_size_buffer);
+
+ // Пишем неизвестное значение #3
+ let mut unknown2_buffer: [u8; 4] = [0; 4];
+ LittleEndian::write_u32(&mut unknown2_buffer, element.unknown2);
+ element_buffer.extend(unknown2_buffer);
+
+ // Пишем название файла
+ let mut name_buffer: [u8; 36] = [0; 36];
+ let mut file_name_buffer = element.name.into_bytes();
+ file_name_buffer.resize(36, 0);
+ name_buffer.copy_from_slice(&file_name_buffer);
+ element_buffer.extend(name_buffer);
+
+ // Пишем позицию файла
+ let mut position_buffer: [u8; 4] = [0; 4];
+ LittleEndian::write_u32(&mut position_buffer, element.position);
+ element_buffer.extend(position_buffer);
+
+ // Пишем индекс файла
+ let mut index_buffer: [u8; 4] = [0; 4];
+ LittleEndian::write_u32(&mut index_buffer, element.index);
+ element_buffer.extend(index_buffer);
+
+ // Добавляем итоговый буфер в буфер элементов списка
+ list_buffer.extend(element_buffer);
+ }
+
+ let mut header_buffer: Vec<u8> = Vec::new();
+
+ // Пишем первый тип файла
+ let mut header_type_1 = [0; 4];
+ LittleEndian::write_u32(&mut header_type_1, 1936020046_u32);
+ header_buffer.extend(header_type_1);
+
+ // Пишем второй тип файла
+ let mut header_type_2 = [0; 4];
+ LittleEndian::write_u32(&mut header_type_2, 256_u32);
+ header_buffer.extend(header_type_2);
+
+ // Пишем количество файлов
+ let mut header_total_files = [0; 4];
+ LittleEndian::write_u32(&mut header_total_files, total_files);
+ header_buffer.extend(header_total_files);
+
+ // Пишем общий размер файла
+ let mut header_total_size = [0; 4];
+ let total_size: u32 = ((content_buffer.len() + 16) as u32) + (total_files * 64);
+ LittleEndian::write_u32(&mut header_total_size, total_size);
+ header_buffer.extend(header_total_size);
+
+ let mut result_buffer: Vec<u8> = Vec::new();
+ result_buffer.extend(header_buffer);
+ result_buffer.extend(content_buffer);
+ result_buffer.extend(list_buffer);
+
+ fs::write(output, result_buffer).unwrap();
+}
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();
+}