use std::collections::HashMap; use std::io; use byteorder::{ReadBytesExt, LE}; use crate::framework::error::GameError::ResourceLoadError; use crate::framework::error::GameResult; #[derive(Debug)] pub struct BmChar { pub x: u16, pub y: u16, pub width: u16, pub height: u16, pub xoffset: i16, pub yoffset: i16, pub xadvance: i16, pub page: u8, pub chnl: u8, } #[derive(Debug)] pub struct BMFont { pub pages: u16, pub font_size: i16, pub line_height: u16, pub base: u16, pub chars: HashMap, } const MAGIC: [u8; 4] = [b'B', b'M', b'F', 3]; impl BMFont { pub fn load_from(mut data: R) -> GameResult { let mut magic = [0u8; 4]; let mut pages = 0u16; let mut chars = HashMap::with_capacity(128); let mut font_size = 0i16; let mut line_height = 0u16; let mut base = 0u16; data.read_exact(&mut magic)?; if magic != MAGIC { return Err(ResourceLoadError("Invalid magic".to_owned())); } while let Ok(block_type) = data.read_u8() { let length = data.read_u32::()?; match block_type { 1 => { font_size = data.read_i16::()?; data.seek(io::SeekFrom::Current(length as i64 - 2))?; } 2 => { line_height = data.read_u16::()?; base = data.read_u16::()?; data.seek(io::SeekFrom::Current(4))?; pages = data.read_u16::()?; data.seek(io::SeekFrom::Current(length as i64 - 10))?; } 3 | 5 => { data.seek(io::SeekFrom::Current(length as i64))?; } 4 => { let count = length / 20; for _ in 0..count { let id = data.read_u32::()?; let x = data.read_u16::()?; let y = data.read_u16::()?; let width = data.read_u16::()?; let height = data.read_u16::()?; let xoffset = data.read_i16::()?; let yoffset = data.read_i16::()?; let xadvance = data.read_i16::()?; let page = data.read_u8()?; let chnl = data.read_u8()?; if let Some(chr) = std::char::from_u32(id) { chars.insert(chr, BmChar { x, y, width, height, xoffset, yoffset, xadvance, page, chnl }); } } } _ => { return Err(ResourceLoadError("Unknown block type.".to_owned())); } } } Ok(Self { pages, font_size, line_height, base, chars }) } }