nte_patcher 0.1.2

Rust implementation of NTE PatcherSDK
Documentation
use crate::error::Error;
use md5::{Digest, Md5};
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;

fn parse_expected_md5(hex_str: &str) -> Option<[u8; 16]> {
    let mut bytes = [0u8; 16];
    hex::decode_to_slice(hex_str, &mut bytes).ok()?;
    Some(bytes)
}

pub async fn check_file_md5(path: &Path, expected_md5: &str) -> Result<bool, Error> {
    let expected_bytes = match parse_expected_md5(expected_md5) {
        Some(b) => b,
        None => return Ok(false),
    };

    let path_buf = path.to_path_buf();

    let is_match = tokio::task::spawn_blocking(move || -> Result<bool, std::io::Error> {
        let mut file = match std::fs::File::open(&path_buf) {
            Ok(f) => f,
            Err(_) => return Ok(false),
        };
        let mut hasher = Md5::new();
        let mut buffer = [0; 131_072];

        loop {
            let n = file.read(&mut buffer)?;
            if n == 0 {
                break;
            }
            hasher.update(&buffer[..n]);
        }

        Ok(hasher.finalize().as_slice() == expected_bytes)
    })
    .await
    .unwrap_or(Ok(false))?;

    Ok(is_match)
}

pub async fn check_slice_md5(
    path: &Path,
    start: u64,
    size: u64,
    expected_md5: &str,
) -> Result<bool, Error> {
    let expected_bytes = match parse_expected_md5(expected_md5) {
        Some(b) => b,
        None => return Ok(false),
    };

    let path_buf = path.to_path_buf();

    let is_match = tokio::task::spawn_blocking(move || -> Result<bool, std::io::Error> {
        let mut file = match std::fs::File::open(&path_buf) {
            Ok(f) => f,
            Err(_) => return Ok(false),
        };

        if file.metadata()?.len() < start + size {
            return Ok(false);
        }

        file.seek(SeekFrom::Start(start))?;

        let mut hasher = Md5::new();
        let mut buffer = [0; 131_072];
        let mut remaining = size;

        while remaining > 0 {
            let to_read = std::cmp::min(remaining, buffer.len() as u64) as usize;
            let n = file.read(&mut buffer[..to_read])?;
            if n == 0 {
                return Ok(false);
            }
            hasher.update(&buffer[..n]);
            remaining -= n as u64;
        }

        Ok(hasher.finalize().as_slice() == expected_bytes)
    })
    .await
    .unwrap_or(Ok(false))?;

    Ok(is_match)
}