use std::collections::HashMap;
use binrw::{binread, BinRead};
use lazy_static::lazy_static;
use resource_fork::Resource;
lazy_static! {
pub static ref WINDOW_COLOR_TABLE_NAMES: HashMap<usize, String> = {
let mut map = HashMap::new();
map.insert(0, "wContentColor".to_string());
map.insert(1, "wFrameColor".to_string());
map.insert(2, "wTextColor".to_string());
map.insert(3, "wHiliteColor".to_string());
map.insert(4, "wTitleBarColor".to_string());
map.insert(5, "wHiliteColorLight".to_string());
map.insert(6, "wHiliteColorDark".to_string());
map.insert(7, "wTitleBarLight".to_string());
map.insert(8, "wTitleBarDark".to_string());
map.insert(9, "wDialogLight".to_string());
map.insert(10, "wDialogDark".to_string());
map.insert(11, "wTingeLight".to_string());
map.insert(12, "wTingeDark".to_string());
map
};
pub static ref CUSTOM_COLOR_TABLE_NAMES: HashMap<usize, String> = {
let mut map = HashMap::new();
map.insert(0, "cFrameColor".to_string());
map.insert(1, "cBodyColor".to_string());
map.insert(2, "extColor".to_string());
map.insert(5, "cArrowsColorLight".to_string());
map.insert(6, "cArrowsColorDark".to_string());
map.insert(7, "cThumbLight".to_string());
map.insert(8, "cThumbDark".to_string());
map.insert(9, "cHiliteLight".to_string());
map.insert(10, "cHiliteDark".to_string());
map.insert(11, "cTitleBarLight".to_string());
map.insert(12, "cTitleBarDark".to_string());
map.insert(13, "cTingeLight".to_string());
map.insert(14, "cTingeDark".to_string());
map
};
}
#[derive(BinRead)]
#[br(big)]
struct ClutHeader {
_unknown: [u16; 3],
#[br(map(|v: u16| v.into()))]
max_color_idx: usize,
}
#[binread]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[serde(into = "ClutEntrySerde")]
#[br(big)]
pub struct ClutEntry {
#[serde(skip)]
color_id: u16,
pub red: u16,
pub green: u16,
pub blue: u16,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[serde(into = "ClutEntrySerde")]
pub struct NamedClutEntry {
pub red: u16,
pub green: u16,
pub blue: u16,
pub name: Option<String>,
}
impl From<NamedClutEntry> for ClutEntrySerde {
fn from(val: NamedClutEntry) -> Self {
ClutEntrySerde {
red: val.red,
green: val.green,
blue: val.blue,
hex: format!(
"#{:02x}{:02x}{:02x}",
((val.red as f32 / u16::MAX as f32) * 0xFF as f32) as u8,
((val.green as f32 / u16::MAX as f32) * 0xFF as f32) as u8,
((val.blue as f32 / u16::MAX as f32) * 0xFF as f32) as u8
),
name: val.name.clone(),
}
}
}
impl From<ClutEntry> for ClutEntrySerde {
fn from(val: ClutEntry) -> Self {
ClutEntrySerde {
red: val.red,
green: val.green,
blue: val.blue,
hex: format!(
"#{:02x}{:02x}{:02x}",
((val.red as f32 / u16::MAX as f32) * 0xFF as f32) as u8,
((val.green as f32 / u16::MAX as f32) * 0xFF as f32) as u8,
((val.blue as f32 / u16::MAX as f32) * 0xFF as f32) as u8
),
name: None,
}
}
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ClutEntrySerde {
pub red: u16,
pub green: u16,
pub blue: u16,
pub hex: String,
pub name: Option<String>,
}
impl ClutEntry {
pub fn to_rgb(&self) -> [u8; 3] {
[
((self.red >> 8) & 0xFF) as u8,
((self.green >> 8) & 0xFF) as u8,
((self.blue >> 8) & 0xFF) as u8,
]
}
pub fn with_name(&self, name: Option<String>) -> NamedClutEntry {
NamedClutEntry {
red: self.red,
green: self.green,
blue: self.blue,
name,
}
}
}
impl std::fmt::Display for ClutEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let [red, green, blue] = self.to_rgb();
write!(f, "Rgb(0x{:02x}, 0x{:02x}, 0x{:02x})", red, green, blue)
}
}
#[binread]
#[derive(Resource)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[resource(code = "clut")]
pub struct Clut {
#[br(temp)]
header: ClutHeader,
#[br(count(header.max_color_idx + 1))]
pub colors: Vec<ClutEntry>,
}
impl Clut {
pub fn rgb_colors(&self) -> Vec<[u8; 3]> {
self.colors.iter().map(|c| c.to_rgb()).collect()
}
pub fn with_names(&self, names: &HashMap<usize, String>) -> NamedClut {
NamedClut {
colors: self
.colors
.iter()
.enumerate()
.map(|(idx, c)| c.with_name(names.get(&idx).cloned()))
.collect(),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NamedClut {
pub colors: Vec<NamedClutEntry>,
}
#[binread]
#[derive(Resource)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[resource(code = "cctb")]
pub struct CustomColorTable(Clut);
#[cfg(feature = "serde")]
impl serde::Serialize for CustomColorTable {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0
.with_names(&CUSTOM_COLOR_TABLE_NAMES)
.serialize(serializer)
}
}