ps3decrs 2.0.1

PS3 ISO decryption tool.
Documentation
use aes::cipher::consts::U16;
use aes::cipher::generic_array::GenericArray;
use chrono::Local;
use log::{info, warn, LevelFilter};
use std::fs::File;
use std::{fs, io};
use std::io::{ Read, Seek, SeekFrom};
use log4rs::{
    append::{console::ConsoleAppender, file::FileAppender},
    config::{Appender, Config, Root},
    encode::pattern::PatternEncoder,
};


#[cfg(unix)]
use std::os::unix::fs::FileExt as _;
#[cfg(windows)]
use std::os::windows::fs::FileExt as _;
use std::path::Path;


#[cfg(unix)]
pub fn read_exact_at(file: &File, mut buf: &mut [u8], mut off: u64) -> io::Result<()> {
    while !buf.is_empty() {
        let n = file.read_at(buf, off)?;
        if n == 0 { return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "read_at=0")); }
        buf = &mut buf[n..];
        off += n as u64;
    }
    Ok(())
}
#[cfg(windows)]
pub fn read_exact_at(file: &File, mut buf: &mut [u8], mut off: u64) -> io::Result<()> {
    while !buf.is_empty() {
        let n = file.seek_read(buf, off)?;
        if n == 0 { return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "seek_read=0")); }
        buf = &mut buf[n..];
        off += n as u64;
    }
    Ok(())
}

#[cfg(unix)]
pub fn write_all_at(file: &File, mut buf: &[u8], mut off: u64) -> io::Result<()> {
    while !buf.is_empty() {
        let n = file.write_at(buf, off)?;
        if n == 0 { return Err(io::Error::new(io::ErrorKind::WriteZero, "write_at=0")); }
        buf = &buf[n..];
        off += n as u64;
    }
    Ok(())
}
#[cfg(windows)]
pub fn write_all_at(file: &File, mut buf: &[u8], mut off: u64) -> io::Result<()> {
    while !buf.is_empty() {
        let n = file.seek_write(buf, off)?;
        if n == 0 { return Err(io::Error::new(io::ErrorKind::WriteZero, "seek_write=0")); }
        buf = &buf[n..];
        off += n as u64;
    }
    Ok(())
}


pub struct Region {
    start: u64,
    end: u64,
}

// Reliability is uncertain on key validation.
pub fn key_validation(key: &str) -> bool {
    let stripped_key: String = key.chars().filter(|c| !c.is_whitespace()).collect();

    if stripped_key.len() != 32 {
        println!("Invalid key length: {}", stripped_key.len());
        warn!("Invalid key length: {}", stripped_key.len());
        return false;
    }

    if !stripped_key.chars().all(|c| c.is_ascii_hexdigit()) {
        println!("Key contains invalid characters");
        warn!("Key contains invalid characters");
        return false;
    }

    info!("Key is valid");
    true
}

// Making an init vector
#[inline(always)]
pub fn generate_iv(sector: u64) -> GenericArray<u8, U16> {
    let mut iv_bytes = [0u8; 16];
    iv_bytes[12] = (sector >> 24) as u8;
    iv_bytes[13] = (sector >> 16) as u8;
    iv_bytes[14] = (sector >> 8) as u8;
    iv_bytes[15] = sector as u8;
    GenericArray::clone_from_slice(&iv_bytes)
}
#[inline(always)]
pub fn is_encrypted(regions: &[Region], sector: u64, sector_data: &[u8]) -> bool {
    if sector_data.iter().all(|&b| b == 0) {
        return false;
    }
    regions.iter().any(|r| sector >= r.start && sector < r.end)
}



// Splitting the cake
pub fn extract_regions<R: Read + Seek>(reader: &mut R) -> io::Result<Vec<Region>> {
    let mut header = [0u8; 4096];
    reader.seek(SeekFrom::Start(0))?;
    reader.read_exact(&mut header)?;
    let num_normal_regions = u32::from_be_bytes(header[0..4].try_into().unwrap()) as usize;
    let regions_count = (num_normal_regions * 2) - 1;
    let mut regions = Vec::with_capacity(regions_count);

    let mut is_encrypted = false;
    for i in 0..regions_count {
        let region_offset = 4 + i * 8;
        let start_sector =
            u32::from_be_bytes(header[region_offset..region_offset + 4].try_into().unwrap());
        let end_sector = u32::from_be_bytes(
            header[region_offset + 4..region_offset + 8]
                .try_into()
                .unwrap(),
        );

        regions.push(Region {
            start: start_sector as u64,
            end: end_sector as u64,
        });

        is_encrypted = !is_encrypted;
    }

    Ok(regions)
}


pub fn setup_logging() -> Result<(), Box<dyn std::error::Error>> {
    let log_dir = Path::new("log");
    fs::create_dir_all(log_dir)?;
    let now = Local::now();
    let log_file_name = format!("log/{}.log", now.format("%Y-%m-%d_%H-%M-%S"));

    let fmt = "{d(%Y-%m-%d %H:%M:%S)} [{l}] - {m}\n";

    let stdout = ConsoleAppender::builder()
        .encoder(Box::new(PatternEncoder::new(fmt)))
        .build();

    let logfile = FileAppender::builder()
        .encoder(Box::new(PatternEncoder::new(fmt)))
        .build(log_file_name)?;

    let config = Config::builder()
        .appender(Appender::builder().build("stdout", Box::new(stdout)))
        .appender(Appender::builder().build("logfile", Box::new(logfile)))
        .build(
            Root::builder()
                .appender("stdout")
                .appender("logfile")
                .build(LevelFilter::Trace),
        )?;

    log4rs::init_config(config)?;
    Ok(())
}