use anyhow::Result; use byteorder::{LittleEndian, ReadBytesExt}; use serde::{Deserialize, Serialize}; use std::io::{Cursor, Read}; use std::path::Path; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HueEntry { pub id: u32, pub name: String, /// 32 colors as RGBA u32 values. pub colors: Vec, } /// Read all hues from hues.mul. /// Format: 375 blocks × 8 hues = 3000 hues. /// Each hue: 32×u16 colors + u16 tableStart + u16 tableEnd + 20-byte name. pub fn read_hues(hues_path: &Path) -> Result> { let data = std::fs::read(hues_path)?; let mut cursor = Cursor::new(&data); let mut hues = Vec::new(); for block in 0..375u32 { // Block header (4 bytes, skip) let _header = cursor.read_u32::()?; for h in 0..8u32 { let id = block * 8 + h; let mut raw_colors = [0u16; 32]; for c in raw_colors.iter_mut() { *c = cursor.read_u16::()?; } let _table_start = cursor.read_u16::()?; let _table_end = cursor.read_u16::()?; let mut name_buf = [0u8; 20]; cursor.read_exact(&mut name_buf)?; let name = String::from_utf8_lossy(&name_buf) .trim_end_matches('\0') .to_string(); let colors = raw_colors .iter() .map(|&c| { let r = ((c >> 10) & 0x1F) as u32; let g = ((c >> 5) & 0x1F) as u32; let b = (c & 0x1F) as u32; let r = (r << 3) | (r >> 2); let g = (g << 3) | (g >> 2); let b = (b << 3) | (b >> 2); (r << 24) | (g << 16) | (b << 8) | 0xFF }) .collect(); hues.push(HueEntry { id, name, colors }); } } Ok(hues) }