summaryrefslogtreecommitdiffstats
path: root/build.rs
blob: 44e634405ab3b3ecc941aff3073a7930a252cbe3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/// Build script — extracts glyph data from a PSF2 font file.
/// Uses zcat (system gzip) to decompress — zero dependencies.
use std::env;
use std::fs;
use std::path::Path;
use std::process::{Command, Output};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let font_path = "/usr/share/kbd/consolefonts/lat1-16.psfu.gz";
    println!("cargo::rerun-if-changed={}", font_path);

    // Decompress via zcat (available on any Linux system)
    let Output { stdout, status, .. } = Command::new("zcat").arg(font_path).output()?;
    if !status.success() {
        return Err(format!("zcat exited with {status:?}").into());
    }
    let raw = stdout;

    if raw.len() < 32 {
        return Err("PSF font too small".into());
    }

    // PSF2 magic: 0x864ab572 (little-endian)
    let magic = u32::from_le_bytes(raw[..4].try_into()?);
    if magic != 0x864ab572 {
        return Err("not a PSF2 font".into());
    }

    let hdr_size =     u32::from_le_bytes(raw[8..12].try_into()?) as usize;
    let num_glyphs =   u32::from_le_bytes(raw[16..20].try_into()?) as usize;
    let bytes_per_glyph = u32::from_le_bytes(raw[20..24].try_into()?) as usize;

    if num_glyphs != 256 {
        return Err(format!("expected 256 glyphs, got {num_glyphs}").into());
    }
    if bytes_per_glyph != 16 {
        return Err(format!("expected 16 bytes/glyph, got {bytes_per_glyph}").into());
    }

    let glyph_data = &raw[hdr_size..hdr_size + num_glyphs * bytes_per_glyph];

    // Write generated Rust source into OUT_DIR
    let out_dir = env::var("OUT_DIR")?;
    let dest = Path::new(&out_dir).join("font_data.rs");

    let mut code = String::from(
        "/// 8×16 bitmap font glyph data (256 glyphs, 16 bytes each).
#[rustfmt::skip]
#[allow(clippy::unreadable_literal)]
pub const GLYPH_8X16: [[u8; 16]; 256] = [",
    );

    for g in 0..256 {
        let offset = g * 16;
        code.push_str("\n    [");
        for row in 0..16 {
            if row % 4 == 0 {
                code.push('\n');
            }
            let byte = glyph_data[offset + row];
            code.push_str(&format!(" 0x{byte:02X},"));
        }
        code.push_str("\n    ],");
    }

    code.push_str("\n];\n");

    fs::write(&dest, code)?;
    Ok(())
}