use std::{
io::{Cursor, Read, Seek, Write},
path::{Path, PathBuf},
};
use crate::{
PlaystationArchive, PlaystationFile,
error::{Error, Result},
toc::TableOfContentEntry,
};
pub(crate) trait BasicFileEntry {
fn index_list_size(&self) -> u32;
fn input_size(&self) -> usize;
fn output_size(&self) -> usize;
fn offset(&self) -> usize;
}
#[derive(Debug, Clone)]
pub(crate) struct FileEntry {
pub(crate) path: PathBuf,
pub(crate) index_list_size: u32,
pub(crate) size: usize,
pub(crate) offset: usize,
pub(crate) input_size: usize,
}
impl FileEntry {
pub(crate) fn from_table_of_content(
TableOfContentEntry {
index_list_size,
size,
offset,
input_size,
}: TableOfContentEntry,
path: &str,
) -> Self {
let path = PathBuf::from(path);
Self {
path,
index_list_size,
size,
offset,
input_size,
}
}
}
impl BasicFileEntry for FileEntry {
fn index_list_size(&self) -> u32 {
self.index_list_size
}
fn input_size(&self) -> usize {
self.input_size
}
fn output_size(&self) -> usize {
self.size
}
fn offset(&self) -> usize {
self.offset
}
}
impl<R: Read + Seek> PlaystationArchive<R> {
pub fn by_index(&mut self, file_index: usize) -> Result<PlaystationFile> {
let entry = self
.file_entries
.get(file_index)
.ok_or(Error::FileAtIndexDoesNotExist(file_index))?;
self.by_entry(&entry.clone())
}
pub fn by_name(&mut self, name: &str) -> Result<PlaystationFile> {
let entry = self.entry_by_name(name)?.clone();
self.by_entry(&entry)
}
pub fn by_path<P>(&mut self, path: P) -> Result<PlaystationFile>
where
P: AsRef<Path>,
{
let entry = self.entry_by_path(path)?.clone();
self.by_entry(&entry)
}
pub(crate) fn by_entry(&mut self, entry: &FileEntry) -> Result<PlaystationFile> {
let mut bytes = vec![0; entry.input_size()];
let mut writer = Cursor::new(&mut bytes);
self.write_bytes_by_entry(&mut writer, entry)?;
Ok(PlaystationFile {
bytes,
path: entry.path.clone(),
})
}
pub(crate) fn write_bytes_by_entry<W>(
&mut self,
writer: &mut W,
entry: &FileEntry,
) -> Result<()>
where
W: Write,
{
self.compression_type.decompress(
&mut self.reader,
writer,
entry,
self.block_size,
&self.block_sizes,
)
}
}
impl PlaystationFile {
#[must_use]
pub fn path(&self) -> &Path {
&self.path
}
#[must_use]
pub fn into_inner(self) -> Vec<u8> {
self.bytes
}
}