use std::{
fmt,
io::{self, SeekFrom},
};
use byteorder::{ReadBytesExt as _, LE};
use indexmap::IndexMap;
use ltk_hash::BinHash;
use crate::{Bin, BinObject, BinOverride, Error};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BinKind {
Prop,
Override,
}
impl BinKind {
#[must_use]
pub const fn magic(self) -> [u8; 4] {
match self {
Self::Prop => *b"PROP",
Self::Override => *b"PTCH",
}
}
#[must_use]
pub fn from_magic(magic: [u8; 4]) -> Option<Self> {
match &magic {
b"PROP" => Some(Self::Prop),
b"PTCH" => Some(Self::Override),
_ => None,
}
}
#[must_use]
pub fn identify_from_bytes(data: &[u8]) -> Option<Self> {
Self::from_magic(data.get(..4)?.try_into().ok()?)
}
pub fn identify_from_reader<R: io::Read + io::Seek + ?Sized>(
reader: &mut R,
) -> Result<Self, Error> {
let start = reader.stream_position()?;
let magic = reader.read_u32::<LE>()?;
reader.seek(SeekFrom::Start(start))?;
Self::from_magic_u32(magic).ok_or(Error::InvalidFileSignature)
}
pub(crate) fn from_magic_u32(magic: u32) -> Option<Self> {
Self::from_magic(magic.to_le_bytes())
}
}
impl fmt::Display for BinKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Prop => "PROP",
Self::Override => "PTCH",
})
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub enum BinFile {
Prop(Bin),
Override(BinOverride),
}
impl BinFile {
pub fn from_reader<R: io::Read + io::Seek + ?Sized>(reader: &mut R) -> Result<Self, Error> {
match BinKind::identify_from_reader(reader)? {
BinKind::Prop => Ok(Self::Prop(Bin::from_reader(reader)?)),
BinKind::Override => Ok(Self::Override(BinOverride::from_reader(reader)?)),
}
}
}
impl BinFile {
#[must_use]
#[inline]
pub fn kind(&self) -> BinKind {
match self {
Self::Prop(_) => BinKind::Prop,
Self::Override(_) => BinKind::Override,
}
}
#[must_use]
#[inline]
pub fn is_prop(&self) -> bool {
matches!(self, Self::Prop(_))
}
#[must_use]
#[inline]
pub fn is_override(&self) -> bool {
matches!(self, Self::Override(_))
}
#[must_use]
#[inline]
pub fn as_prop(&self) -> Option<&Bin> {
match self {
Self::Prop(bin) => Some(bin),
Self::Override(_) => None,
}
}
#[must_use]
#[inline]
pub fn as_prop_mut(&mut self) -> Option<&mut Bin> {
match self {
Self::Prop(bin) => Some(bin),
Self::Override(_) => None,
}
}
#[must_use]
#[inline]
pub fn into_prop(self) -> Option<Bin> {
match self {
Self::Prop(bin) => Some(bin),
Self::Override(_) => None,
}
}
#[must_use]
#[inline]
pub fn as_override(&self) -> Option<&BinOverride> {
match self {
Self::Override(patch_bin) => Some(patch_bin),
Self::Prop(_) => None,
}
}
#[must_use]
#[inline]
pub fn as_override_mut(&mut self) -> Option<&mut BinOverride> {
match self {
Self::Override(patch_bin) => Some(patch_bin),
Self::Prop(_) => None,
}
}
#[must_use]
#[inline]
pub fn into_override(self) -> Option<BinOverride> {
match self {
Self::Override(patch_bin) => Some(patch_bin),
Self::Prop(_) => None,
}
}
#[must_use]
#[inline]
pub fn objects(&self) -> &IndexMap<BinHash, BinObject> {
match self {
Self::Prop(bin) => &bin.objects,
Self::Override(patch_bin) => &patch_bin.objects,
}
}
#[must_use]
#[inline]
pub fn objects_mut(&mut self) -> &mut IndexMap<BinHash, BinObject> {
match self {
Self::Prop(bin) => &mut bin.objects,
Self::Override(patch_bin) => &mut patch_bin.objects,
}
}
}
impl BinFile {
pub fn to_writer<W: io::Write + io::Seek + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
match self {
Self::Prop(bin) => bin.to_writer(writer),
Self::Override(patch_bin) => patch_bin.to_writer(writer),
}
}
}
impl From<Bin> for BinFile {
fn from(value: Bin) -> Self {
Self::Prop(value)
}
}
impl From<BinOverride> for BinFile {
fn from(value: BinOverride) -> Self {
Self::Override(value)
}
}