summaryrefslogtreecommitdiff
path: root/quantum/painter/qff.c
blob: 8590f5b4009988086166e32722509058d36b4ec5 (plain)
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright 2021 Nick Brassel (@tzarc)
// SPDX-License-Identifier: GPL-2.0-or-later

// Quantum Font File "QFF" File Format.
// See https://docs.qmk.fm/#/quantum_painter_qff for more information.

#include "qff.h"
#include "qp_draw.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// QFF API

bool qff_read_font_descriptor(qp_stream_t *stream, uint8_t *line_height, bool *has_ascii_table, uint16_t *num_unicode_glyphs, uint8_t *bpp, bool *has_palette, bool *is_panel_native, painter_compression_t *compression_scheme, uint32_t *total_bytes) {
    // Seek to the start
    qp_stream_setpos(stream, 0);

    // Read and validate the font descriptor
    qff_font_descriptor_v1_t font_descriptor;
    if (qp_stream_read(&font_descriptor, sizeof(qff_font_descriptor_v1_t), 1, stream) != 1) {
        qp_dprintf("Failed to read font_descriptor, expected length was not %d\n", (int)sizeof(qff_font_descriptor_v1_t));
        return false;
    }

    // Make sure this block is valid
    if (!qgf_validate_block_header(&font_descriptor.header, QFF_FONT_DESCRIPTOR_TYPEID, (sizeof(qff_font_descriptor_v1_t) - sizeof(qgf_block_header_v1_t)))) {
        return false;
    }

    // Make sure the magic and version are correct
    if (font_descriptor.magic != QFF_MAGIC || font_descriptor.qff_version != 0x01) {
        qp_dprintf("Failed to validate font_descriptor, expected magic 0x%06X was 0x%06X, expected version = 0x%02X was 0x%02X\n", (int)QFF_MAGIC, (int)font_descriptor.magic, (int)0x01, (int)font_descriptor.qff_version);
        return false;
    }

    // Make sure the file length is valid
    if (font_descriptor.neg_total_file_size != ~font_descriptor.total_file_size) {
        qp_dprintf("Failed to validate font_descriptor, expected negated length 0x%08X was 0x%08X\n", (int)(~font_descriptor.total_file_size), (int)font_descriptor.neg_total_file_size);
        return false;
    }

    // Copy out the required info
    if (line_height) {
        *line_height = font_descriptor.line_height;
    }
    if (has_ascii_table) {
        *has_ascii_table = font_descriptor.has_ascii_table;
    }
    if (num_unicode_glyphs) {
        *num_unicode_glyphs = font_descriptor.num_unicode_glyphs;
    }
    if (bpp || has_palette) {
        if (!qgf_parse_format(font_descriptor.format, bpp, has_palette, is_panel_native)) {
            return false;
        }
    }
    if (compression_scheme) {
        *compression_scheme = font_descriptor.compression_scheme;
    }
    if (total_bytes) {
        *total_bytes = font_descriptor.total_file_size;
    }

    return true;
}

static bool qff_validate_ascii_descriptor(qp_stream_t *stream) {
    // Read the raw descriptor
    qff_ascii_glyph_table_v1_t ascii_descriptor;
    if (qp_stream_read(&ascii_descriptor, sizeof(qff_ascii_glyph_table_v1_t), 1, stream) != 1) {
        qp_dprintf("Failed to read ascii_descriptor, expected length was not %d\n", (int)sizeof(qff_ascii_glyph_table_v1_t));
        return false;
    }

    // Make sure this block is valid
    if (!qgf_validate_block_header(&ascii_descriptor.header, QFF_ASCII_GLYPH_DESCRIPTOR_TYPEID, (sizeof(qff_ascii_glyph_table_v1_t) - sizeof(qgf_block_header_v1_t)))) {
        return false;
    }

    return true;
}

static bool qff_validate_unicode_descriptor(qp_stream_t *stream, uint16_t num_unicode_glyphs) {
    // Read the raw descriptor
    qff_unicode_glyph_table_v1_t unicode_descriptor;
    if (qp_stream_read(&unicode_descriptor, sizeof(qff_unicode_glyph_table_v1_t), 1, stream) != 1) {
        qp_dprintf("Failed to read unicode_descriptor, expected length was not %d\n", (int)sizeof(qff_unicode_glyph_table_v1_t));
        return false;
    }

    // Make sure this block is valid
    if (!qgf_validate_block_header(&unicode_descriptor.header, QFF_UNICODE_GLYPH_DESCRIPTOR_TYPEID, num_unicode_glyphs * 6)) {
        return false;
    }

    // Skip the necessary amount of data to get to the next block
    qp_stream_seek(stream, num_unicode_glyphs * sizeof(qff_unicode_glyph_v1_t), SEEK_CUR);

    return true;
}

bool qff_validate_stream(qp_stream_t *stream) {
    bool     has_ascii_table;
    uint16_t num_unicode_glyphs;

    if (!qff_read_font_descriptor(stream, NULL, &has_ascii_table, &num_unicode_glyphs, NULL, NULL, NULL, NULL, NULL)) {
        return false;
    }

    if (has_ascii_table) {
        if (!qff_validate_ascii_descriptor(stream)) {
            return false;
        }
    }

    if (num_unicode_glyphs > 0) {
        if (!qff_validate_unicode_descriptor(stream, num_unicode_glyphs)) {
            return false;
        }
    }

    return true;
}

uint32_t qff_get_total_size(qp_stream_t *stream) {
    // Get the original location
    uint32_t oldpos = qp_stream_tell(stream);

    // Read the font descriptor, grabbing the size
    uint32_t total_size;
    if (!qff_read_font_descriptor(stream, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &total_size)) {
        return false;
    }

    // Restore the original location
    qp_stream_setpos(stream, oldpos);
    return total_size;
}