Перенос наработок в новый репозиторий
This commit is contained in:
parent
a7fd7ba7e9
commit
4e195ec386
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
105
Cargo.lock
generated
Normal file
105
Cargo.lock
generated
Normal file
@ -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",
|
||||||
|
]
|
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[workspace]
|
||||||
|
members = ["packer", "unpacker"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
lto = true
|
||||||
|
strip = true
|
9
packer/Cargo.toml
Normal file
9
packer/Cargo.toml
Normal file
@ -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"
|
0
packer/README.md
Normal file
0
packer/README.md
Normal file
170
packer/src/main.rs
Normal file
170
packer/src/main.rs
Normal file
@ -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();
|
||||||
|
}
|
9
unpacker/Cargo.toml
Normal file
9
unpacker/Cargo.toml
Normal file
@ -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"
|
0
unpacker/README.md
Normal file
0
unpacker/README.md
Normal file
124
unpacker/src/main.rs
Normal file
124
unpacker/src/main.rs
Normal file
@ -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();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user