mod bsa;
mod file;
mod io;
mod set;
#[cfg(test)]
mod tests;
pub use file::StringFile;
pub use set::{StringFileSet, StringFileStats};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum StringFileType {
DLSTRINGS,
ILSTRINGS,
STRINGS,
}
impl StringFileType {
pub fn from_extension(extension: &str) -> Option<Self> {
match extension.to_uppercase().as_str() {
"DLSTRINGS" => Some(StringFileType::DLSTRINGS),
"ILSTRINGS" => Some(StringFileType::ILSTRINGS),
"STRINGS" => Some(StringFileType::STRINGS),
_ => None,
}
}
pub fn to_extension(&self) -> &'static str {
match self {
StringFileType::DLSTRINGS => "DLSTRINGS",
StringFileType::ILSTRINGS => "ILSTRINGS",
StringFileType::STRINGS => "STRINGS",
}
}
pub fn has_length_prefix(&self) -> bool {
matches!(self, StringFileType::DLSTRINGS | StringFileType::ILSTRINGS)
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StringEntry {
pub id: u32,
pub directory_address: u64,
pub relative_offset: u32,
pub absolute_offset: u64,
pub length: Option<u32>,
pub content: String,
pub raw_data: Vec<u8>,
}
impl StringEntry {
pub fn new(id: u32, content: String) -> Self {
let raw_data = content.as_bytes().to_vec();
Self {
id,
directory_address: 0,
relative_offset: 0,
absolute_offset: 0,
length: Some(raw_data.len() as u32),
content,
raw_data,
}
}
pub fn get_total_size(&self, file_type: &StringFileType) -> u32 {
let content_size = self.content.len() as u32;
let null_terminator = 1u32;
if file_type.has_length_prefix() {
4 + content_size + null_terminator } else {
content_size + null_terminator }
}
}