Обновление структуры проекта
This commit is contained in:
		
							
								
								
									
										14
									
								
								tools/nres-cli/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tools/nres-cli/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "nres-cli"
 | 
			
		||||
version = "0.2.3"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
byteorder = "1.4"
 | 
			
		||||
clap = { version = "4.2", features = ["derive"] }
 | 
			
		||||
console = "0.15"
 | 
			
		||||
dialoguer = { version = "0.11", features = ["completion"] }
 | 
			
		||||
indicatif = "0.17"
 | 
			
		||||
libnres = { version = "0.1", path = "../../libs/nres" }
 | 
			
		||||
miette = { version = "7.0", features = ["fancy"] }
 | 
			
		||||
tempdir = "0.3"
 | 
			
		||||
							
								
								
									
										6
									
								
								tools/nres-cli/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tools/nres-cli/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
# Console tool for NRes files (Deprecated)
 | 
			
		||||
 | 
			
		||||
## Commands
 | 
			
		||||
 | 
			
		||||
- `extract` - Extract game resources from a "NRes" file.
 | 
			
		||||
- `ls` - Get a list of files in a "NRes" file.
 | 
			
		||||
							
								
								
									
										198
									
								
								tools/nres-cli/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								tools/nres-cli/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,198 @@
 | 
			
		||||
extern crate core;
 | 
			
		||||
extern crate libnres;
 | 
			
		||||
 | 
			
		||||
use std::io::Write;
 | 
			
		||||
 | 
			
		||||
use clap::{Parser, Subcommand};
 | 
			
		||||
use miette::{IntoDiagnostic, Result};
 | 
			
		||||
 | 
			
		||||
#[derive(Parser, Debug)]
 | 
			
		||||
#[command(name = "NRes CLI")]
 | 
			
		||||
#[command(about, author, version, long_about = None)]
 | 
			
		||||
struct Cli {
 | 
			
		||||
    #[command(subcommand)]
 | 
			
		||||
    command: Commands,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Subcommand, Debug)]
 | 
			
		||||
enum Commands {
 | 
			
		||||
    /// Check if the "NRes" file can be extract
 | 
			
		||||
    Check {
 | 
			
		||||
        /// "NRes" file
 | 
			
		||||
        file: String,
 | 
			
		||||
    },
 | 
			
		||||
    /// Print debugging information on the "NRes" file
 | 
			
		||||
    #[command(arg_required_else_help = true)]
 | 
			
		||||
    Debug {
 | 
			
		||||
        /// "NRes" file
 | 
			
		||||
        file: String,
 | 
			
		||||
        /// Filter results by file name
 | 
			
		||||
        #[arg(long)]
 | 
			
		||||
        name: Option<String>,
 | 
			
		||||
    },
 | 
			
		||||
    /// Extract files or a file from the "NRes" file
 | 
			
		||||
    #[command(arg_required_else_help = true)]
 | 
			
		||||
    Extract {
 | 
			
		||||
        /// "NRes" file
 | 
			
		||||
        file: String,
 | 
			
		||||
        /// Overwrite files
 | 
			
		||||
        #[arg(short, long, default_value_t = false, value_name = "TRUE|FALSE")]
 | 
			
		||||
        force: bool,
 | 
			
		||||
        /// Outbound directory
 | 
			
		||||
        #[arg(short, long, value_name = "DIR")]
 | 
			
		||||
        out: String,
 | 
			
		||||
    },
 | 
			
		||||
    /// Print a list of files in the "NRes" file
 | 
			
		||||
    #[command(arg_required_else_help = true)]
 | 
			
		||||
    Ls {
 | 
			
		||||
        /// "NRes" file
 | 
			
		||||
        file: String,
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn main() -> Result<()> {
 | 
			
		||||
    let stdout = console::Term::stdout();
 | 
			
		||||
    let cli = Cli::parse();
 | 
			
		||||
 | 
			
		||||
    match cli.command {
 | 
			
		||||
        Commands::Check { file } => command_check(stdout, file)?,
 | 
			
		||||
        Commands::Debug { file, name } => command_debug(stdout, file, name)?,
 | 
			
		||||
        Commands::Extract { file, force, out } => command_extract(stdout, file, out, force)?,
 | 
			
		||||
        Commands::Ls { file } => command_ls(stdout, file)?,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn command_check(_stdout: console::Term, file: String) -> Result<()> {
 | 
			
		||||
    let file = std::fs::File::open(file).into_diagnostic()?;
 | 
			
		||||
    let list = libnres::reader::get_list(&file).into_diagnostic()?;
 | 
			
		||||
    let tmp = tempdir::TempDir::new("nres").into_diagnostic()?;
 | 
			
		||||
    let bar = indicatif::ProgressBar::new(list.len() as u64);
 | 
			
		||||
 | 
			
		||||
    bar.set_style(get_bar_style()?);
 | 
			
		||||
 | 
			
		||||
    for element in list {
 | 
			
		||||
        bar.set_message(element.get_filename());
 | 
			
		||||
 | 
			
		||||
        let path = tmp.path().join(element.get_filename());
 | 
			
		||||
        let mut output = std::fs::File::create(path).into_diagnostic()?;
 | 
			
		||||
        let mut buffer = libnres::reader::get_file(&file, &element).into_diagnostic()?;
 | 
			
		||||
 | 
			
		||||
        output.write_all(&buffer).into_diagnostic()?;
 | 
			
		||||
        buffer.clear();
 | 
			
		||||
        bar.inc(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bar.finish();
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn command_debug(stdout: console::Term, file: String, name: Option<String>) -> Result<()> {
 | 
			
		||||
    let file = std::fs::File::open(file).into_diagnostic()?;
 | 
			
		||||
    let mut list = libnres::reader::get_list(&file).into_diagnostic()?;
 | 
			
		||||
 | 
			
		||||
    let mut total_files_size: u32 = 0;
 | 
			
		||||
    let mut total_files_gap: u32 = 0;
 | 
			
		||||
    let mut total_files: u32 = 0;
 | 
			
		||||
 | 
			
		||||
    for (index, item) in list.iter().enumerate() {
 | 
			
		||||
        total_files_size += item.size;
 | 
			
		||||
        total_files += 1;
 | 
			
		||||
        let mut gap = 0;
 | 
			
		||||
 | 
			
		||||
        if index > 1 {
 | 
			
		||||
            let previous_item = &list[index - 1];
 | 
			
		||||
            gap = item.position - (previous_item.position + previous_item.size);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        total_files_gap += gap;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if let Some(name) = name {
 | 
			
		||||
        list.retain(|item| item.name.contains(&name));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for (index, item) in list.iter().enumerate() {
 | 
			
		||||
        let mut gap = 0;
 | 
			
		||||
 | 
			
		||||
        if index > 1 {
 | 
			
		||||
            let previous_item = &list[index - 1];
 | 
			
		||||
            gap = item.position - (previous_item.position + previous_item.size);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let text = format!("Index: {};\nGap: {};\nItem: {:#?};\n", index, gap, item);
 | 
			
		||||
        stdout.write_line(&text).into_diagnostic()?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let text = format!(
 | 
			
		||||
        "Total files: {};\nTotal files gap: {} (bytes);\nTotal files size: {} (bytes);",
 | 
			
		||||
        total_files, total_files_gap, total_files_size
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    stdout.write_line(&text).into_diagnostic()?;
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn command_extract(_stdout: console::Term, file: String, out: String, force: bool) -> Result<()> {
 | 
			
		||||
    let file = std::fs::File::open(file).into_diagnostic()?;
 | 
			
		||||
    let list = libnres::reader::get_list(&file).into_diagnostic()?;
 | 
			
		||||
    let bar = indicatif::ProgressBar::new(list.len() as u64);
 | 
			
		||||
 | 
			
		||||
    bar.set_style(get_bar_style()?);
 | 
			
		||||
 | 
			
		||||
    for element in list {
 | 
			
		||||
        bar.set_message(element.get_filename());
 | 
			
		||||
 | 
			
		||||
        let path = format!("{}/{}", out, element.get_filename());
 | 
			
		||||
 | 
			
		||||
        if !force && is_exist_file(&path) {
 | 
			
		||||
            let message = format!("File \"{}\" exists. Overwrite it?", path);
 | 
			
		||||
 | 
			
		||||
            if !dialoguer::Confirm::new()
 | 
			
		||||
                .with_prompt(message)
 | 
			
		||||
                .interact()
 | 
			
		||||
                .into_diagnostic()?
 | 
			
		||||
            {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut output = std::fs::File::create(path).into_diagnostic()?;
 | 
			
		||||
        let mut buffer = libnres::reader::get_file(&file, &element).into_diagnostic()?;
 | 
			
		||||
 | 
			
		||||
        output.write_all(&buffer).into_diagnostic()?;
 | 
			
		||||
        buffer.clear();
 | 
			
		||||
        bar.inc(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bar.finish();
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn command_ls(stdout: console::Term, file: String) -> Result<()> {
 | 
			
		||||
    let file = std::fs::File::open(file).into_diagnostic()?;
 | 
			
		||||
    let list = libnres::reader::get_list(&file).into_diagnostic()?;
 | 
			
		||||
 | 
			
		||||
    for element in list {
 | 
			
		||||
        stdout.write_line(&element.name).into_diagnostic()?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_bar_style() -> Result<indicatif::ProgressStyle> {
 | 
			
		||||
    Ok(
 | 
			
		||||
        indicatif::ProgressStyle::with_template("[{bar:32}] {pos:>7}/{len:7} {msg}")
 | 
			
		||||
            .into_diagnostic()?
 | 
			
		||||
            .progress_chars("=>-"),
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn is_exist_file(path: &String) -> bool {
 | 
			
		||||
    let metadata = std::path::Path::new(path);
 | 
			
		||||
    metadata.exists()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										8
									
								
								tools/texture-decoder/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tools/texture-decoder/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "texture-decoder"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
byteorder = "1.4.3"
 | 
			
		||||
image = "0.25.0"
 | 
			
		||||
							
								
								
									
										13
									
								
								tools/texture-decoder/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								tools/texture-decoder/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
# Декодировщик текстур
 | 
			
		||||
 | 
			
		||||
Сборка:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
cargo build --release
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Запуск:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
./target/release/texture-decoder ./out/AIM_02.0 ./out/AIM_02.0.png
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										41
									
								
								tools/texture-decoder/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								tools/texture-decoder/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
use std::io::Read;
 | 
			
		||||
 | 
			
		||||
use byteorder::ReadBytesExt;
 | 
			
		||||
use image::Rgba;
 | 
			
		||||
 | 
			
		||||
fn decode_texture(file_path: &str, output_path: &str) -> Result<(), std::io::Error> {
 | 
			
		||||
    // Читаем файл
 | 
			
		||||
    let mut file = std::fs::File::open(file_path)?;
 | 
			
		||||
    let mut buffer: Vec<u8> = Vec::new();
 | 
			
		||||
    file.read_to_end(&mut buffer)?;
 | 
			
		||||
 | 
			
		||||
    // Декодируем метаданные
 | 
			
		||||
    let mut cursor = std::io::Cursor::new(&buffer[4..]);
 | 
			
		||||
    let img_width = cursor.read_u32::<byteorder::LittleEndian>()?;
 | 
			
		||||
    let img_height = cursor.read_u32::<byteorder::LittleEndian>()?;
 | 
			
		||||
 | 
			
		||||
    // Пропустить оставшиеся байты метаданных
 | 
			
		||||
    cursor.set_position(20);
 | 
			
		||||
 | 
			
		||||
    // Извлекаем данные изображения
 | 
			
		||||
    let image_data = buffer[cursor.position() as usize..].to_vec();
 | 
			
		||||
    let img =
 | 
			
		||||
        image::ImageBuffer::<Rgba<u8>, _>::from_raw(img_width, img_height, image_data.to_vec())
 | 
			
		||||
            .expect("Failed to decode image");
 | 
			
		||||
 | 
			
		||||
    // Сохраняем изображение
 | 
			
		||||
    img.save(output_path).unwrap();
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    let args: Vec<String> = std::env::args().collect();
 | 
			
		||||
 | 
			
		||||
    let input = &args[1];
 | 
			
		||||
    let output = &args[2];
 | 
			
		||||
 | 
			
		||||
    if let Err(err) = decode_texture(input, output) {
 | 
			
		||||
        eprintln!("Error: {}", err)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								tools/unpacker/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tools/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"
 | 
			
		||||
							
								
								
									
										41
									
								
								tools/unpacker/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								tools/unpacker/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
# NRes Game Resource Unpacker
 | 
			
		||||
 | 
			
		||||
At the moment, this is a demonstration of the NRes game resource unpacking algorithm in action.
 | 
			
		||||
It unpacks 100% of the NRes game resources for the game "Parkan: Iron Strategy".
 | 
			
		||||
The unpacked resources can be packed again using the [packer](../packer) utility and replace the original game files.
 | 
			
		||||
 | 
			
		||||
__Attention!__
 | 
			
		||||
This is a test version of the utility.
 | 
			
		||||
It overwrites existing files without asking.
 | 
			
		||||
 | 
			
		||||
## Building
 | 
			
		||||
 | 
			
		||||
To build the tools, you need to run the following command in the root directory:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
cargo build --release
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Running
 | 
			
		||||
 | 
			
		||||
You can run the utility with the following command:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
./target/release/unpacker /path/to/file.ex /path/to/output
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
- `/path/to/file.ex`: This is the file containing the game resources that will be unpacked.
 | 
			
		||||
- `/path/to/output`: This is the directory where the unpacked files will be placed.
 | 
			
		||||
 | 
			
		||||
## How it Works
 | 
			
		||||
 | 
			
		||||
The structure describing the packed game resources is not fully understood yet.
 | 
			
		||||
Therefore, the utility saves unpacked files in the format `file_name.file_index` because some files have the same name.
 | 
			
		||||
 | 
			
		||||
Additionally, an `index.json` file is created, which is important for re-packing the files.
 | 
			
		||||
This file lists all the fields that game resources have in their packed form.
 | 
			
		||||
It is essential to preserve the file index for the game to function correctly, as the game engine looks for the necessary files by index.
 | 
			
		||||
 | 
			
		||||
Files can be replaced and packed back using the [packer](../packer).
 | 
			
		||||
The newly obtained game resource files are correctly processed by the game engine.
 | 
			
		||||
For example, sounds and 3D models of warbots' weapons were successfully replaced.
 | 
			
		||||
							
								
								
									
										124
									
								
								tools/unpacker/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								tools/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, element.index);
 | 
			
		||||
        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();
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user