use crate::error::{Error, Result};
use bytestream::{ByteOrder, StreamReader};
use semver::{Version, VersionReq};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek, SeekFrom, Write};
use self::qol::Indexizable;
pub mod bccad;
pub mod brcad;
#[cfg(feature = "modder_qol")]
pub mod qol;
pub const OLDEST_SUPPORTED_VERSION: &str = "2.0.0-pre1";
pub trait BXCAD: Sized {
const BYTE_ORDER: ByteOrder;
const TIMESTAMP: u32;
const BXCAD_TYPE: BXCADType;
fn from_binary<F: Read + Seek>(f: &mut F) -> Result<Self>;
fn to_binary<F: Write>(&self, f: &mut F) -> Result<()>;
fn is_format<F: Read + Seek>(f: &mut F) -> Result<bool> {
let timestamp = u32::read_from(f, Self::BYTE_ORDER)?;
f.seek(SeekFrom::Current(-4))?;
Ok(timestamp == Self::TIMESTAMP)
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
#[non_exhaustive]
pub enum BXCADType {
BRCAD,
BCCAD,
}
pub fn get_bxcad_type<F: Read + Seek>(f: &mut F) -> Result<Option<BXCADType>> {
Ok(if bccad::BCCAD::is_format(f)? {
Some(BXCADType::BCCAD)
} else if brcad::BRCAD::is_format(f)? {
Some(BXCADType::BRCAD)
} else {
None
})
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct PosInTexture {
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
}
#[derive(Serialize, Deserialize)]
#[non_exhaustive]
pub struct BXCADWrapper<X> {
pub bxcad_type: BXCADType,
pub flour_version: String,
#[serde(default)]
pub indexize: bool,
data: X,
}
impl<X: BXCAD> BXCADWrapper<X> {
pub fn from_bxcad(bxcad: X) -> Self {
Self {
bxcad_type: X::BXCAD_TYPE,
flour_version: env!("CARGO_PKG_VERSION").to_string(),
indexize: false,
data: bxcad,
}
}
pub fn to_bxcad(self) -> Result<X> {
let requirement = VersionReq::parse(&format!(
"<={}, >={}",
env!("CARGO_PKG_VERSION"),
OLDEST_SUPPORTED_VERSION
))?;
let version = Version::parse(&self.flour_version)?;
if !requirement.matches(&version) {
return Err(Error::IncompatibleVersion(version));
}
Ok(self.data)
}
}
#[cfg(feature = "modder_qol")]
impl<I> BXCADWrapper<I> {
pub fn indexized_to_bxcad<X: Indexizable<Indexized = I>>(self) -> Result<X> {
let requirement = VersionReq::parse(&format!(
"<={}, >={}",
env!("CARGO_PKG_VERSION"),
OLDEST_SUPPORTED_VERSION
))?;
let version = Version::parse(&self.flour_version)?;
if !requirement.matches(&version) {
return Err(Error::IncompatibleVersion(version));
}
Ok(X::from_indexized(self.data))
}
}
#[cfg(feature = "modder_qol")]
impl<X: qol::Indexizable> BXCADWrapper<X> {
pub fn from_bxcad_indexize(bxcad: X) -> BXCADWrapper<X::Indexized> {
let data = bxcad.to_indexized();
BXCADWrapper {
bxcad_type: X::BXCAD_TYPE,
flour_version: env!("CARGO_PKG_VERSION").to_string(),
indexize: true,
data,
}
}
}