#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![forbid(unsafe_code)]
#![allow(clippy::useless_conversion)]
#![allow(non_camel_case_types)]
#![warn(
clippy::perf,
clippy::correctness,
clippy::style,
clippy::missing_const_for_fn,
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::doc_markdown,
clippy::unseparated_literal_suffix,
missing_docs
)]
use std::io::{Read, Write};
pub(crate) mod error;
pub(crate) mod header;
#[allow(missing_docs)]
#[cfg(feature = "vanilla")]
pub mod vanilla_tables;
#[allow(missing_docs)]
#[cfg(feature = "tbc")]
pub mod tbc_tables;
#[allow(missing_docs)]
#[cfg(feature = "wrath")]
pub mod wrath_tables;
mod util;
pub use error::*;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[cfg(any(feature = "tbc", feature = "wrath"))]
pub struct ExtendedLocalizedString {
pub en_gb: String,
pub ko_kr: String,
pub fr_fr: String,
pub de_de: String,
pub en_cn: String,
pub en_tw: String,
pub es_es: String,
pub es_mx: String,
pub ru_ru: String,
pub ja_jp: String,
pub pt_pt: String,
pub it_it: String,
pub unknown_12: String,
pub unknown_13: String,
pub unknown_14: String,
pub unknown_15: String,
pub flags: u32,
}
#[cfg(any(feature = "tbc", feature = "wrath"))]
impl ExtendedLocalizedString {
#[allow(clippy::too_many_arguments)]
pub(crate) const fn new(
en_gb: String,
ko_kr: String,
fr_fr: String,
de_de: String,
en_cn: String,
en_tw: String,
es_es: String,
es_mx: String,
ru_ru: String,
ja_jp: String,
pt_pt: String,
it_it: String,
unknown_12: String,
unknown_13: String,
unknown_14: String,
unknown_15: String,
flags: u32,
) -> Self {
Self {
en_gb,
ko_kr,
fr_fr,
de_de,
en_cn,
en_tw,
es_es,
es_mx,
ru_ru,
ja_jp,
pt_pt,
it_it,
unknown_12,
unknown_13,
unknown_14,
unknown_15,
flags,
}
}
pub(crate) fn string_indices_as_array(&self, string_index: &mut usize) -> [u8; 16 * 4 + 4] {
let mut arr = [0_u8; 16 * 4 + 4]; let mut index = 0;
for s in self.strings() {
let value = (if !s.is_empty() {
let v = *string_index;
*string_index += s.len() + 1;
v
} else {
0
} as u32)
.to_le_bytes();
arr[index] = value[0];
arr[index + 1] = value[1];
arr[index + 2] = value[2];
arr[index + 3] = value[3];
index += 4;
}
let value = &self.flags.to_le_bytes();
arr[index] = value[0];
arr[index + 1] = value[1];
arr[index + 2] = value[2];
arr[index + 3] = value[3];
arr
}
pub(crate) fn string_block_as_array(&self, b: &mut impl Write) -> Result<(), std::io::Error> {
for s in self.strings() {
if !s.is_empty() {
b.write_all(s.as_bytes())?;
b.write_all(&[0])?;
};
}
Ok(())
}
pub(crate) fn string_block_size(&self) -> usize {
let mut sum = 0;
for s in self.strings() {
if !s.is_empty() {
sum += s.len() + 1;
}
}
sum
}
pub(crate) const fn strings(&self) -> [&String; 16] {
[
&self.en_gb,
&self.ko_kr,
&self.fr_fr,
&self.de_de,
&self.en_cn,
&self.en_tw,
&self.es_es,
&self.es_mx,
&self.ru_ru,
&self.ja_jp,
&self.pt_pt,
&self.it_it,
&self.unknown_12,
&self.unknown_13,
&self.unknown_14,
&self.unknown_15,
]
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct LocalizedString {
pub en_gb: String,
pub ko_kr: String,
pub fr_fr: String,
pub de_de: String,
pub en_cn: String,
pub en_tw: String,
pub es_es: String,
pub es_mx: String,
pub flags: u32,
}
impl LocalizedString {
#[allow(clippy::too_many_arguments)]
pub(crate) const fn new(
en_gb: String,
ko_kr: String,
fr_fr: String,
de_de: String,
en_cn: String,
en_tw: String,
es_es: String,
es_mx: String,
flags: u32,
) -> Self {
Self {
en_gb,
ko_kr,
fr_fr,
de_de,
en_cn,
en_tw,
es_es,
es_mx,
flags,
}
}
pub(crate) fn string_indices_as_array(&self, string_index: &mut usize) -> [u8; 36] {
let mut arr = [0_u8; 4 * 8 + 4]; let mut index = 0;
for s in self.strings() {
let value = (if !s.is_empty() {
let v = *string_index;
*string_index += s.len() + 1;
v
} else {
0
} as u32)
.to_le_bytes();
arr[index] = value[0];
arr[index + 1] = value[1];
arr[index + 2] = value[2];
arr[index + 3] = value[3];
index += 4;
}
let value = &self.flags.to_le_bytes();
arr[index] = value[0];
arr[index + 1] = value[1];
arr[index + 2] = value[2];
arr[index + 3] = value[3];
arr
}
pub(crate) fn string_block_as_array(&self, b: &mut impl Write) -> Result<(), std::io::Error> {
for s in self.strings() {
if !s.is_empty() {
b.write_all(s.as_bytes())?;
b.write_all(&[0])?;
};
}
Ok(())
}
pub(crate) fn string_block_size(&self) -> usize {
let mut sum = 0;
for s in self.strings() {
if !s.is_empty() {
sum += s.len() + 1;
}
}
sum
}
pub(crate) const fn strings(&self) -> [&String; 8] {
[
&self.en_gb,
&self.ko_kr,
&self.fr_fr,
&self.de_de,
&self.en_cn,
&self.en_tw,
&self.es_es,
&self.es_mx,
]
}
}
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum Gender {
Male,
Female,
}
impl TryFrom<i32> for Gender {
type Error = InvalidEnumError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
TryFrom::try_from(value as i8)
}
}
impl TryFrom<i8> for Gender {
type Error = InvalidEnumError;
fn try_from(value: i8) -> Result<Self, Self::Error> {
Ok(match value {
0 => Self::Male,
1 => Self::Female,
val => return Err(InvalidEnumError::new("Gender", val as i64)),
})
}
}
impl Gender {
const fn as_int(&self) -> i32 {
match self {
Self::Male => 0,
Self::Female => 1,
}
}
}
impl Default for Gender {
fn default() -> Self {
Self::Male
}
}
#[allow(missing_docs)]
#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum SizeClass {
None,
Small,
Medium,
Large,
Giant,
Colossal,
}
impl TryFrom<i32> for SizeClass {
type Error = InvalidEnumError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
Ok(match value {
-1 => Self::None,
0 => Self::Small,
1 => Self::Medium,
2 => Self::Large,
3 => Self::Giant,
4 => Self::Colossal,
val => return Err(InvalidEnumError::new("SizeClass", val as i64)),
})
}
}
impl SizeClass {
const fn as_int(&self) -> i32 {
match self {
Self::None => -1,
Self::Small => 0,
Self::Medium => 1,
Self::Large => 2,
Self::Giant => 3,
Self::Colossal => 4,
}
}
}
impl Default for SizeClass {
fn default() -> Self {
Self::None
}
}
pub trait DbcTable: Sized {
type Row;
fn filename() -> &'static str;
fn rows(&self) -> &[Self::Row];
fn rows_mut(&mut self) -> &mut [Self::Row];
fn read(b: &mut impl Read) -> Result<Self, DbcError>;
fn write(&self, w: &mut impl Write) -> Result<(), std::io::Error>;
}
pub trait Indexable: DbcTable {
type PrimaryKey;
fn get(&self, key: impl Into<Self::PrimaryKey>) -> Option<&Self::Row>;
fn get_mut(&mut self, key: impl Into<Self::PrimaryKey>) -> Option<&mut Self::Row>;
}