use std::{
ffi::OsStr,
io::{Read, Seek},
path::{Path, PathBuf},
};
use aes::{Aes256, cipher::KeyIvInit as _};
use cfb_mode::Decryptor;
use crate::{
PlaystationArchive,
error::{Error, Result},
file::FileEntry,
toc::DecryptionKey,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ArchiveFlags {
Relative,
IgnoreCase,
Absolute,
Encrypted,
}
impl ArchiveFlags {
pub(crate) fn decrypt(
self,
buffer: &mut [u8],
decryption_key: Option<DecryptionKey>,
) -> Result<()> {
if self == Self::Encrypted {
let Some(DecryptionKey { key, iv }) = decryption_key else {
return Err(Error::MissingDecryptionKey);
};
Decryptor::<Aes256>::new(&key.into(), &iv.into()).decrypt(buffer);
}
Ok(())
}
}
impl TryFrom<u32> for ArchiveFlags {
type Error = Error;
fn try_from(value: u32) -> Result<Self> {
match value {
0 => Ok(Self::Relative),
1 => Ok(Self::IgnoreCase),
2 => Ok(Self::Absolute),
4 => Ok(Self::Encrypted),
_ => Err(Error::Corrupt("unrecognized archive flags")),
}
}
}
impl<R: Read + Seek> PlaystationArchive<R> {
pub fn paths(&self) -> impl Iterator<Item = &'_ Path> {
self.file_entries
.iter()
.skip(1)
.map(|entry| entry.path.as_path())
}
pub fn index_for_name(&self, name: &str) -> Option<usize> {
let (index, _entry) = self
.file_entries
.iter()
.enumerate()
.find(|(_index, entry)| {
entry
.path
.file_name()
.is_some_and(|file_name| file_name == name)
})?;
Some(index)
}
pub fn index_for_path<P>(&self, path: P) -> Option<usize>
where
P: AsRef<Path>,
{
let path = path.as_ref();
let (index, _entry) = self
.file_entries
.iter()
.enumerate()
.find(|(_index, entry)| entry.path == path)?;
Some(index)
}
pub fn name_for_index<P>(&self, index: usize) -> Option<&OsStr> {
self.file_entries.get(index)?.path.file_name()
}
pub(crate) fn entry_by_name(&self, name: &str) -> Result<&FileEntry> {
self.file_entries
.iter()
.find(|entry| {
entry
.path
.file_name()
.is_some_and(|file_name| file_name == name)
})
.ok_or_else(|| Error::FileAtPathDoesNotExist(PathBuf::from(name)))
}
pub(crate) fn entry_by_path<P>(&self, path: P) -> Result<&FileEntry>
where
P: AsRef<Path>,
{
let path = path.as_ref();
self.file_entries
.iter()
.find(|entry| entry.path == path)
.ok_or_else(|| Error::FileAtPathDoesNotExist(path.to_path_buf()))
}
}