qgf.c (11598B)
1 // Copyright 2021 Nick Brassel (@tzarc) 2 // SPDX-License-Identifier: GPL-2.0-or-later 3 4 // Quantum Graphics File "QGF" File Format. 5 // See https://docs.qmk.fm/#/quantum_painter_qgf for more information. 6 7 #include "qgf.h" 8 #include "qp_draw.h" 9 10 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 11 // QGF API 12 13 bool qgf_validate_block_header(qgf_block_header_v1_t *desc, uint8_t expected_typeid, int32_t expected_length) { 14 if (desc->type_id != expected_typeid || desc->neg_type_id != ((~expected_typeid) & 0xFF)) { 15 qp_dprintf("Failed to validate header, expected typeid 0x%02X, was 0x%02X, expected negated typeid 0x%02X, was 0x%02X\n", (int)expected_typeid, (int)desc->type_id, (int)((~desc->type_id) & 0xFF), (int)desc->neg_type_id); 16 return false; 17 } 18 19 if (expected_length >= 0 && desc->length != expected_length) { 20 qp_dprintf("Failed to validate header (typeid 0x%02X), expected length %d, was %d\n", (int)desc->type_id, (int)expected_length, (int)desc->length); 21 return false; 22 } 23 24 return true; 25 } 26 27 bool qgf_parse_format(qp_image_format_t format, uint8_t *bpp, bool *has_palette, bool *is_panel_native) { 28 // clang-format off 29 static const struct PACKED { 30 uint8_t bpp; 31 bool has_palette; 32 bool is_panel_native; 33 } formats[] = { 34 [GRAYSCALE_1BPP] = {.bpp = 1, .has_palette = false, .is_panel_native = false}, 35 [GRAYSCALE_2BPP] = {.bpp = 2, .has_palette = false, .is_panel_native = false}, 36 [GRAYSCALE_4BPP] = {.bpp = 4, .has_palette = false, .is_panel_native = false}, 37 [GRAYSCALE_8BPP] = {.bpp = 8, .has_palette = false, .is_panel_native = false}, 38 [PALETTE_1BPP] = {.bpp = 1, .has_palette = true, .is_panel_native = false}, 39 [PALETTE_2BPP] = {.bpp = 2, .has_palette = true, .is_panel_native = false}, 40 [PALETTE_4BPP] = {.bpp = 4, .has_palette = true, .is_panel_native = false}, 41 [PALETTE_8BPP] = {.bpp = 8, .has_palette = true, .is_panel_native = false}, 42 [RGB565_16BPP] = {.bpp = 16, .has_palette = false, .is_panel_native = true}, 43 [RGB888_24BPP] = {.bpp = 24, .has_palette = false, .is_panel_native = true}, 44 }; 45 // clang-format on 46 47 // Copy out the required info 48 if (format > RGB888_24BPP) { 49 qp_dprintf("Failed to parse frame_descriptor, invalid format 0x%02X\n", (int)format); 50 return false; 51 } 52 53 // Copy out the required info 54 if (bpp) { 55 *bpp = formats[format].bpp; 56 } 57 if (has_palette) { 58 *has_palette = formats[format].has_palette; 59 } 60 if (is_panel_native) { 61 *is_panel_native = formats[format].is_panel_native; 62 } 63 64 return true; 65 } 66 67 bool qgf_parse_frame_descriptor(qgf_frame_v1_t *frame_descriptor, uint8_t *bpp, bool *has_palette, bool *is_panel_native, bool *is_delta, painter_compression_t *compression_scheme, uint16_t *delay) { 68 // Decode the format 69 qgf_parse_format(frame_descriptor->format, bpp, has_palette, is_panel_native); 70 71 // Copy out the required info 72 if (is_delta) { 73 *is_delta = (frame_descriptor->flags & QGF_FRAME_FLAG_DELTA) == QGF_FRAME_FLAG_DELTA; 74 } 75 if (compression_scheme) { 76 *compression_scheme = frame_descriptor->compression_scheme; 77 } 78 if (delay) { 79 *delay = frame_descriptor->delay; 80 } 81 82 return true; 83 } 84 85 bool qgf_read_graphics_descriptor(qp_stream_t *stream, uint16_t *image_width, uint16_t *image_height, uint16_t *frame_count, uint32_t *total_bytes) { 86 // Seek to the start 87 qp_stream_setpos(stream, 0); 88 89 // Read and validate the graphics descriptor 90 qgf_graphics_descriptor_v1_t graphics_descriptor; 91 if (qp_stream_read(&graphics_descriptor, sizeof(qgf_graphics_descriptor_v1_t), 1, stream) != 1) { 92 qp_dprintf("Failed to read graphics_descriptor, expected length was not %d\n", (int)sizeof(qgf_graphics_descriptor_v1_t)); 93 return false; 94 } 95 96 // Make sure this block is valid 97 if (!qgf_validate_block_header(&graphics_descriptor.header, QGF_GRAPHICS_DESCRIPTOR_TYPEID, (sizeof(qgf_graphics_descriptor_v1_t) - sizeof(qgf_block_header_v1_t)))) { 98 return false; 99 } 100 101 // Make sure the magic and version are correct 102 if (graphics_descriptor.magic != QGF_MAGIC || graphics_descriptor.qgf_version != 0x01) { 103 qp_dprintf("Failed to validate graphics_descriptor, expected magic 0x%06X was 0x%06X, expected version = 0x%02X was 0x%02X\n", (int)QGF_MAGIC, (int)graphics_descriptor.magic, (int)0x01, (int)graphics_descriptor.qgf_version); 104 return false; 105 } 106 107 // Make sure the file length is valid 108 if (graphics_descriptor.neg_total_file_size != ~graphics_descriptor.total_file_size) { 109 qp_dprintf("Failed to validate graphics_descriptor, expected negated length 0x%08X was 0x%08X\n", (int)(~graphics_descriptor.total_file_size), (int)graphics_descriptor.neg_total_file_size); 110 return false; 111 } 112 113 // Copy out the required info 114 if (image_width) { 115 *image_width = graphics_descriptor.image_width; 116 } 117 if (image_height) { 118 *image_height = graphics_descriptor.image_height; 119 } 120 if (frame_count) { 121 *frame_count = graphics_descriptor.frame_count; 122 } 123 if (total_bytes) { 124 *total_bytes = graphics_descriptor.total_file_size; 125 } 126 127 return true; 128 } 129 130 static bool qgf_read_frame_offset(qp_stream_t *stream, uint16_t frame_number, uint32_t *frame_offset) { 131 uint16_t frame_count; 132 if (!qgf_read_graphics_descriptor(stream, NULL, NULL, &frame_count, NULL)) { 133 return false; 134 } 135 136 // Read the frame offsets descriptor 137 qgf_frame_offsets_v1_t frame_offsets; 138 if (qp_stream_read(&frame_offsets, sizeof(qgf_frame_offsets_v1_t), 1, stream) != 1) { 139 qp_dprintf("Failed to read frame_offsets, expected length was not %d\n", (int)sizeof(qgf_frame_offsets_v1_t)); 140 return false; 141 } 142 143 // Make sure this block is valid 144 if (!qgf_validate_block_header(&frame_offsets.header, QGF_FRAME_OFFSET_DESCRIPTOR_TYPEID, (frame_count * sizeof(uint32_t)))) { 145 return false; 146 } 147 148 if (frame_number >= frame_count) { 149 qp_dprintf("Invalid frame number, was %d but only %d frames in image\n", (int)frame_number, (int)frame_count); 150 return false; 151 } 152 153 // Skip the necessary amount of data to get to the requested frame offset 154 qp_stream_seek(stream, frame_number * sizeof(uint32_t), SEEK_CUR); 155 156 // Read the frame offset 157 uint32_t offset = 0; 158 if (qp_stream_read(&offset, sizeof(uint32_t), 1, stream) != 1) { 159 qp_dprintf("Failed to read frame offset, expected length was not %d\n", (int)sizeof(uint32_t)); 160 return false; 161 } 162 163 // Copy out the required info 164 if (frame_offset) { 165 *frame_offset = offset; 166 } 167 168 return true; 169 } 170 171 void qgf_seek_to_frame_descriptor(qp_stream_t *stream, uint16_t frame_number) { 172 // Read the offset 173 uint32_t offset = 0; 174 qgf_read_frame_offset(stream, frame_number, &offset); 175 176 // Move to the offset 177 qp_stream_setpos(stream, offset); 178 } 179 180 bool qgf_validate_frame_descriptor(qp_stream_t *stream, uint16_t frame_number, uint8_t *bpp, bool *has_palette, bool *is_panel_native, bool *is_delta) { 181 // Seek to the correct location 182 qgf_seek_to_frame_descriptor(stream, frame_number); 183 184 // Read the raw descriptor 185 qgf_frame_v1_t frame_descriptor; 186 if (qp_stream_read(&frame_descriptor, sizeof(qgf_frame_v1_t), 1, stream) != 1) { 187 qp_dprintf("Failed to read frame_descriptor, expected length was not %d\n", (int)sizeof(qgf_frame_v1_t)); 188 return false; 189 } 190 191 // Make sure this block is valid 192 if (!qgf_validate_block_header(&frame_descriptor.header, QGF_FRAME_DESCRIPTOR_TYPEID, (sizeof(qgf_frame_v1_t) - sizeof(qgf_block_header_v1_t)))) { 193 return false; 194 } 195 196 return qgf_parse_frame_descriptor(&frame_descriptor, bpp, has_palette, is_panel_native, is_delta, NULL, NULL); 197 } 198 199 bool qgf_validate_palette_descriptor(qp_stream_t *stream, uint16_t frame_number, uint8_t bpp) { 200 // Read the palette descriptor 201 qgf_palette_v1_t palette_descriptor; 202 if (qp_stream_read(&palette_descriptor, sizeof(qgf_palette_v1_t), 1, stream) != 1) { 203 qp_dprintf("Failed to read palette_descriptor, expected length was not %d\n", (int)sizeof(qgf_palette_v1_t)); 204 return false; 205 } 206 207 // Make sure this block is valid 208 uint32_t expected_length = (1 << bpp) * 3 * sizeof(uint8_t); 209 if (!qgf_validate_block_header(&palette_descriptor.header, QGF_FRAME_PALETTE_DESCRIPTOR_TYPEID, expected_length)) { 210 return false; 211 } 212 213 // Move forward in the stream to the next block 214 qp_stream_seek(stream, expected_length, SEEK_CUR); 215 return true; 216 } 217 218 bool qgf_validate_delta_descriptor(qp_stream_t *stream, uint16_t frame_number) { 219 // Read the delta descriptor 220 qgf_delta_v1_t delta_descriptor; 221 if (qp_stream_read(&delta_descriptor, sizeof(qgf_delta_v1_t), 1, stream) != 1) { 222 qp_dprintf("Failed to read delta_descriptor, expected length was not %d\n", (int)sizeof(qgf_delta_v1_t)); 223 return false; 224 } 225 226 // Make sure this block is valid 227 if (!qgf_validate_block_header(&delta_descriptor.header, QGF_FRAME_DELTA_DESCRIPTOR_TYPEID, (sizeof(qgf_delta_v1_t) - sizeof(qgf_block_header_v1_t)))) { 228 return false; 229 } 230 231 return true; 232 } 233 234 bool qgf_validate_frame_data_descriptor(qp_stream_t *stream, uint16_t frame_number) { 235 // Read and validate the data block 236 qgf_data_v1_t data_descriptor; 237 if (qp_stream_read(&data_descriptor, sizeof(qgf_data_v1_t), 1, stream) != 1) { 238 qp_dprintf("Failed to read data_descriptor, expected length was not %d\n", (int)sizeof(qgf_data_v1_t)); 239 return false; 240 } 241 242 if (!qgf_validate_block_header(&data_descriptor.header, QGF_FRAME_DATA_DESCRIPTOR_TYPEID, -1)) { 243 return false; 244 } 245 246 return true; 247 } 248 249 bool qgf_validate_stream(qp_stream_t *stream) { 250 uint16_t frame_count; 251 if (!qgf_read_graphics_descriptor(stream, NULL, NULL, &frame_count, NULL)) { 252 return false; 253 } 254 255 // Read and validate all the frames (automatically validates the frame offset descriptor in the process) 256 for (uint16_t i = 0; i < frame_count; ++i) { 257 // Validate the frame descriptor block 258 uint8_t bpp = 0; 259 bool has_palette = false; 260 bool is_panel_native = false; 261 bool has_delta = false; 262 if (!qgf_validate_frame_descriptor(stream, i, &bpp, &has_palette, &is_panel_native, &has_delta)) { 263 return false; 264 } 265 266 // If we've got a palette block, check it 267 if (has_palette && !qgf_validate_palette_descriptor(stream, i, bpp)) { 268 return false; 269 } 270 271 // If we've got a delta block, check it 272 if (has_delta && !qgf_validate_delta_descriptor(stream, i)) { 273 return false; 274 } 275 276 // Check the data block 277 if (!qgf_validate_frame_data_descriptor(stream, i)) { 278 return false; 279 } 280 } 281 282 return true; 283 } 284 285 // Work out the total size of an image definition, assuming we can read far enough into the file 286 uint32_t qgf_get_total_size(qp_stream_t *stream) { 287 // Get the original location 288 uint32_t oldpos = qp_stream_tell(stream); 289 290 // Read the graphics descriptor, grabbing the size 291 uint32_t total_size; 292 if (!qgf_read_graphics_descriptor(stream, NULL, NULL, NULL, &total_size)) { 293 return false; 294 } 295 296 // Restore the original location 297 qp_stream_setpos(stream, oldpos); 298 return total_size; 299 }