Перенос старых наработок в новый репозиторий
This commit is contained in:
		
							
								
								
									
										33
									
								
								libnres/src/converter.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								libnres/src/converter.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
use crate::error::ConverterError;
 | 
			
		||||
 | 
			
		||||
/// Method for converting u32 to u64.
 | 
			
		||||
pub fn u32_to_u64(value: u32) -> Result<u64, ConverterError> {
 | 
			
		||||
    match u64::try_from(value) {
 | 
			
		||||
        Err(error) => Err(ConverterError::Infallible(error)),
 | 
			
		||||
        Ok(result) => Ok(result),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Method for converting u32 to usize.
 | 
			
		||||
pub fn u32_to_usize(value: u32) -> Result<usize, ConverterError> {
 | 
			
		||||
    match usize::try_from(value) {
 | 
			
		||||
        Err(error) => Err(ConverterError::TryFromIntError(error)),
 | 
			
		||||
        Ok(result) => Ok(result),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Method for converting u64 to u32.
 | 
			
		||||
pub fn u64_to_u32(value: u64) -> Result<u32, ConverterError> {
 | 
			
		||||
    match u32::try_from(value) {
 | 
			
		||||
        Err(error) => Err(ConverterError::TryFromIntError(error)),
 | 
			
		||||
        Ok(result) => Ok(result),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Method for converting usize to u32.
 | 
			
		||||
pub fn usize_to_u32(value: usize) -> Result<u32, ConverterError> {
 | 
			
		||||
    match u32::try_from(value) {
 | 
			
		||||
        Err(error) => Err(ConverterError::TryFromIntError(error)),
 | 
			
		||||
        Ok(result) => Ok(result),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										45
									
								
								libnres/src/error.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								libnres/src/error.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
extern crate miette;
 | 
			
		||||
extern crate thiserror;
 | 
			
		||||
 | 
			
		||||
use miette::Diagnostic;
 | 
			
		||||
use thiserror::Error;
 | 
			
		||||
 | 
			
		||||
#[derive(Error, Diagnostic, Debug)]
 | 
			
		||||
pub enum ConverterError {
 | 
			
		||||
    #[error("error converting an value")]
 | 
			
		||||
    #[diagnostic(code(libnres::infallible))]
 | 
			
		||||
    Infallible(#[from] std::convert::Infallible),
 | 
			
		||||
 | 
			
		||||
    #[error("error converting an value")]
 | 
			
		||||
    #[diagnostic(code(libnres::try_from_int_error))]
 | 
			
		||||
    TryFromIntError(#[from] std::num::TryFromIntError),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Error, Diagnostic, Debug)]
 | 
			
		||||
pub enum ReaderError {
 | 
			
		||||
    #[error(transparent)]
 | 
			
		||||
    #[diagnostic(code(libnres::convert_error))]
 | 
			
		||||
    ConvertValue(#[from] ConverterError),
 | 
			
		||||
 | 
			
		||||
    #[error("incorrect header format")]
 | 
			
		||||
    #[diagnostic(code(libnres::list_type_error))]
 | 
			
		||||
    IncorrectHeader,
 | 
			
		||||
 | 
			
		||||
    #[error("incorrect file size (expected {expected:?} bytes, received {received:?} bytes)")]
 | 
			
		||||
    #[diagnostic(code(libnres::file_size_error))]
 | 
			
		||||
    IncorrectSizeFile { expected: u32, received: u32 },
 | 
			
		||||
 | 
			
		||||
    #[error(
 | 
			
		||||
        "incorrect size of the file list (not a multiple of {expected:?}, received {received:?})"
 | 
			
		||||
    )]
 | 
			
		||||
    #[diagnostic(code(libnres::list_size_error))]
 | 
			
		||||
    IncorrectSizeList { expected: u32, received: u32 },
 | 
			
		||||
 | 
			
		||||
    #[error("resource file reading error")]
 | 
			
		||||
    #[diagnostic(code(libnres::io_error))]
 | 
			
		||||
    ReadFile(#[from] std::io::Error),
 | 
			
		||||
 | 
			
		||||
    #[error("file is too small (must be at least {expected:?} bytes, received {received:?} byte)")]
 | 
			
		||||
    #[diagnostic(code(libnres::file_size_error))]
 | 
			
		||||
    SmallFile { expected: u32, received: u32 },
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										24
									
								
								libnres/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								libnres/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
/// First constant value of the NRes file ("NRes" characters in numeric)
 | 
			
		||||
pub const FILE_TYPE_1: u32 = 1936020046;
 | 
			
		||||
/// Second constant value of the NRes file
 | 
			
		||||
pub const FILE_TYPE_2: u32 = 256;
 | 
			
		||||
/// Size of the element item (in bytes)
 | 
			
		||||
pub const LIST_ELEMENT_SIZE: u32 = 64;
 | 
			
		||||
/// Minimum allowed file size (in bytes)
 | 
			
		||||
pub const MINIMUM_FILE_SIZE: u32 = 16;
 | 
			
		||||
 | 
			
		||||
static DEBUG: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
 | 
			
		||||
 | 
			
		||||
mod converter;
 | 
			
		||||
mod error;
 | 
			
		||||
pub mod reader;
 | 
			
		||||
 | 
			
		||||
/// Get debug status value
 | 
			
		||||
pub fn get_debug() -> bool {
 | 
			
		||||
    DEBUG.load(std::sync::atomic::Ordering::Relaxed)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Change debug status value
 | 
			
		||||
pub fn set_debug(value: bool) {
 | 
			
		||||
    DEBUG.store(value, std::sync::atomic::Ordering::Relaxed)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										227
									
								
								libnres/src/reader.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								libnres/src/reader.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,227 @@
 | 
			
		||||
use std::io::{Read, Seek};
 | 
			
		||||
 | 
			
		||||
use byteorder::ByteOrder;
 | 
			
		||||
 | 
			
		||||
use crate::error::ReaderError;
 | 
			
		||||
use crate::{converter, FILE_TYPE_1, FILE_TYPE_2, LIST_ELEMENT_SIZE, MINIMUM_FILE_SIZE};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct ListElement {
 | 
			
		||||
    /// Unknown parameter
 | 
			
		||||
    _unknown0: i32,
 | 
			
		||||
    /// Unknown parameter
 | 
			
		||||
    _unknown1: i32,
 | 
			
		||||
    /// Unknown parameter
 | 
			
		||||
    _unknown2: i32,
 | 
			
		||||
    /// File extension
 | 
			
		||||
    pub extension: String,
 | 
			
		||||
    /// Identifier or sequence number
 | 
			
		||||
    pub index: u32,
 | 
			
		||||
    /// File name
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    /// Position in the file
 | 
			
		||||
    pub position: u32,
 | 
			
		||||
    /// File size (in bytes)
 | 
			
		||||
    pub size: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ListElement {
 | 
			
		||||
    /// Get full name of the file
 | 
			
		||||
    pub fn get_filename(&self) -> String {
 | 
			
		||||
        format!("{}.{}", self.name, self.extension)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct FileHeader {
 | 
			
		||||
    /// File size
 | 
			
		||||
    size: u32,
 | 
			
		||||
    /// Number of files
 | 
			
		||||
    total: u32,
 | 
			
		||||
    /// First constant value
 | 
			
		||||
    type1: u32,
 | 
			
		||||
    /// Second constant value
 | 
			
		||||
    type2: u32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get a packed file data
 | 
			
		||||
pub fn get_file(file: &std::fs::File, element: &ListElement) -> Result<Vec<u8>, ReaderError> {
 | 
			
		||||
    let size = get_file_size(file)?;
 | 
			
		||||
    check_file_size(size)?;
 | 
			
		||||
 | 
			
		||||
    let header = get_file_header(file)?;
 | 
			
		||||
    check_file_header(&header, size)?;
 | 
			
		||||
 | 
			
		||||
    let data = get_element_data(file, element)?;
 | 
			
		||||
    Ok(data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get a list of packed files
 | 
			
		||||
pub fn get_list(file: &std::fs::File) -> Result<Vec<ListElement>, ReaderError> {
 | 
			
		||||
    let mut list: Vec<ListElement> = Vec::new();
 | 
			
		||||
 | 
			
		||||
    let size = get_file_size(file)?;
 | 
			
		||||
    check_file_size(size)?;
 | 
			
		||||
 | 
			
		||||
    let header = get_file_header(file)?;
 | 
			
		||||
    check_file_header(&header, size)?;
 | 
			
		||||
 | 
			
		||||
    get_file_list(file, &header, &mut list)?;
 | 
			
		||||
 | 
			
		||||
    Ok(list)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn check_file_header(header: &FileHeader, size: u32) -> Result<(), ReaderError> {
 | 
			
		||||
    if header.type1 != FILE_TYPE_1 || header.type2 != FILE_TYPE_2 {
 | 
			
		||||
        return Err(ReaderError::IncorrectHeader);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if header.size != size {
 | 
			
		||||
        return Err(ReaderError::IncorrectSizeFile {
 | 
			
		||||
            expected: size,
 | 
			
		||||
            received: header.size,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn check_file_size(size: u32) -> Result<(), ReaderError> {
 | 
			
		||||
    if size < MINIMUM_FILE_SIZE {
 | 
			
		||||
        return Err(ReaderError::SmallFile {
 | 
			
		||||
            expected: MINIMUM_FILE_SIZE,
 | 
			
		||||
            received: size,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_element_data(file: &std::fs::File, element: &ListElement) -> Result<Vec<u8>, ReaderError> {
 | 
			
		||||
    let position = converter::u32_to_u64(element.position)?;
 | 
			
		||||
    let size = converter::u32_to_usize(element.size)?;
 | 
			
		||||
 | 
			
		||||
    let mut reader = std::io::BufReader::new(file);
 | 
			
		||||
    let mut buffer = vec![0u8; size];
 | 
			
		||||
 | 
			
		||||
    if let Err(error) = reader.seek(std::io::SeekFrom::Start(position)) {
 | 
			
		||||
        return Err(ReaderError::ReadFile(error));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if let Err(error) = reader.read_exact(&mut buffer) {
 | 
			
		||||
        return Err(ReaderError::ReadFile(error));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Ok(buffer)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_element_position(index: u32) -> Result<(usize, usize), ReaderError> {
 | 
			
		||||
    let from = converter::u32_to_usize(index * LIST_ELEMENT_SIZE)?;
 | 
			
		||||
    let to = converter::u32_to_usize((index * LIST_ELEMENT_SIZE) + LIST_ELEMENT_SIZE)?;
 | 
			
		||||
    Ok((from, to))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_file_header(file: &std::fs::File) -> Result<FileHeader, ReaderError> {
 | 
			
		||||
    let mut reader = std::io::BufReader::new(file);
 | 
			
		||||
    let mut buffer = vec![0u8; MINIMUM_FILE_SIZE as usize];
 | 
			
		||||
 | 
			
		||||
    if let Err(error) = reader.seek(std::io::SeekFrom::Start(0)) {
 | 
			
		||||
        return Err(ReaderError::ReadFile(error));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if let Err(error) = reader.read_exact(&mut buffer) {
 | 
			
		||||
        return Err(ReaderError::ReadFile(error));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let header = FileHeader {
 | 
			
		||||
        size: byteorder::LittleEndian::read_u32(&buffer[12..16]),
 | 
			
		||||
        total: byteorder::LittleEndian::read_u32(&buffer[8..12]),
 | 
			
		||||
        type1: byteorder::LittleEndian::read_u32(&buffer[0..4]),
 | 
			
		||||
        type2: byteorder::LittleEndian::read_u32(&buffer[4..8]),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    buffer.clear();
 | 
			
		||||
    Ok(header)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_file_list(
 | 
			
		||||
    file: &std::fs::File,
 | 
			
		||||
    header: &FileHeader,
 | 
			
		||||
    list: &mut Vec<ListElement>,
 | 
			
		||||
) -> Result<(), ReaderError> {
 | 
			
		||||
    let (start_position, list_size) = get_list_position(header)?;
 | 
			
		||||
    let mut reader = std::io::BufReader::new(file);
 | 
			
		||||
    let mut buffer = vec![0u8; list_size];
 | 
			
		||||
 | 
			
		||||
    if let Err(error) = reader.seek(std::io::SeekFrom::Start(start_position)) {
 | 
			
		||||
        return Err(ReaderError::ReadFile(error));
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if let Err(error) = reader.read_exact(&mut buffer) {
 | 
			
		||||
        return Err(ReaderError::ReadFile(error));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let buffer_size = converter::usize_to_u32(buffer.len())?;
 | 
			
		||||
 | 
			
		||||
    if buffer_size % LIST_ELEMENT_SIZE != 0 {
 | 
			
		||||
        return Err(ReaderError::IncorrectSizeList {
 | 
			
		||||
            expected: LIST_ELEMENT_SIZE,
 | 
			
		||||
            received: buffer_size,
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for i in 0..(buffer_size / LIST_ELEMENT_SIZE) {
 | 
			
		||||
        let (from, to) = get_element_position(i)?;
 | 
			
		||||
        let chunk: &[u8] = &buffer[from..to];
 | 
			
		||||
 | 
			
		||||
        let element = get_list_element(chunk)?;
 | 
			
		||||
        list.push(element);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    buffer.clear();
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_file_size(file: &std::fs::File) -> Result<u32, ReaderError> {
 | 
			
		||||
    let metadata = match file.metadata() {
 | 
			
		||||
        Err(error) => return Err(ReaderError::ReadFile(error)),
 | 
			
		||||
        Ok(value) => value,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let result = converter::u64_to_u32(metadata.len())?;
 | 
			
		||||
    Ok(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_list_element(buffer: &[u8]) -> Result<ListElement, ReaderError> {
 | 
			
		||||
    let index = byteorder::LittleEndian::read_u32(&buffer[60..64]);
 | 
			
		||||
    let position = byteorder::LittleEndian::read_u32(&buffer[56..60]);
 | 
			
		||||
    let size = byteorder::LittleEndian::read_u32(&buffer[12..16]);
 | 
			
		||||
    let unknown0 = byteorder::LittleEndian::read_i32(&buffer[4..8]);
 | 
			
		||||
    let unknown1 = byteorder::LittleEndian::read_i32(&buffer[8..12]);
 | 
			
		||||
    let unknown2 = byteorder::LittleEndian::read_i32(&buffer[16..20]);
 | 
			
		||||
 | 
			
		||||
    let extension = String::from_utf8_lossy(&buffer[0..4])
 | 
			
		||||
        .trim_matches(char::from(0))
 | 
			
		||||
        .to_string();
 | 
			
		||||
 | 
			
		||||
    let name = String::from_utf8_lossy(&buffer[20..56])
 | 
			
		||||
        .trim_matches(char::from(0))
 | 
			
		||||
        .to_string();
 | 
			
		||||
 | 
			
		||||
    Ok(ListElement {
 | 
			
		||||
        _unknown0: unknown0,
 | 
			
		||||
        _unknown1: unknown1,
 | 
			
		||||
        _unknown2: unknown2,
 | 
			
		||||
        extension,
 | 
			
		||||
        index,
 | 
			
		||||
        name,
 | 
			
		||||
        position,
 | 
			
		||||
        size,
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn get_list_position(header: &FileHeader) -> Result<(u64, usize), ReaderError> {
 | 
			
		||||
    let position = converter::u32_to_u64(header.size - (header.total * LIST_ELEMENT_SIZE))?;
 | 
			
		||||
    let size = converter::u32_to_usize(header.total * LIST_ELEMENT_SIZE)?;
 | 
			
		||||
    Ok((position, size))
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user