qff.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. // Copyright 2021 Nick Brassel (@tzarc)
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. // Quantum Font File "QFF" File Format.
  4. // See https://docs.qmk.fm/#/quantum_painter_qff for more information.
  5. #include "qff.h"
  6. #include "qp_draw.h"
  7. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  8. // QFF API
  9. 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, painter_compression_t *compression_scheme, uint32_t *total_bytes) {
  10. // Seek to the start
  11. qp_stream_setpos(stream, 0);
  12. // Read and validate the font descriptor
  13. qff_font_descriptor_v1_t font_descriptor;
  14. if (qp_stream_read(&font_descriptor, sizeof(qff_font_descriptor_v1_t), 1, stream) != 1) {
  15. qp_dprintf("Failed to read font_descriptor, expected length was not %d\n", (int)sizeof(qff_font_descriptor_v1_t));
  16. return false;
  17. }
  18. // Make sure this block is valid
  19. if (!qgf_validate_block_header(&font_descriptor.header, QFF_FONT_DESCRIPTOR_TYPEID, (sizeof(qff_font_descriptor_v1_t) - sizeof(qgf_block_header_v1_t)))) {
  20. return false;
  21. }
  22. // Make sure the magic and version are correct
  23. if (font_descriptor.magic != QFF_MAGIC || font_descriptor.qff_version != 0x01) {
  24. 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);
  25. return false;
  26. }
  27. // Make sure the file length is valid
  28. if (font_descriptor.neg_total_file_size != ~font_descriptor.total_file_size) {
  29. 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);
  30. return false;
  31. }
  32. // Copy out the required info
  33. if (line_height) {
  34. *line_height = font_descriptor.line_height;
  35. }
  36. if (has_ascii_table) {
  37. *has_ascii_table = font_descriptor.has_ascii_table;
  38. }
  39. if (num_unicode_glyphs) {
  40. *num_unicode_glyphs = font_descriptor.num_unicode_glyphs;
  41. }
  42. if (bpp || has_palette) {
  43. if (!qgf_parse_format(font_descriptor.format, bpp, has_palette)) {
  44. return false;
  45. }
  46. }
  47. if (compression_scheme) {
  48. *compression_scheme = font_descriptor.compression_scheme;
  49. }
  50. if (total_bytes) {
  51. *total_bytes = font_descriptor.total_file_size;
  52. }
  53. return true;
  54. }
  55. static bool qff_validate_ascii_descriptor(qp_stream_t *stream) {
  56. // Read the raw descriptor
  57. qff_ascii_glyph_table_v1_t ascii_descriptor;
  58. if (qp_stream_read(&ascii_descriptor, sizeof(qff_ascii_glyph_table_v1_t), 1, stream) != 1) {
  59. qp_dprintf("Failed to read ascii_descriptor, expected length was not %d\n", (int)sizeof(qff_ascii_glyph_table_v1_t));
  60. return false;
  61. }
  62. // Make sure this block is valid
  63. 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)))) {
  64. return false;
  65. }
  66. return true;
  67. }
  68. static bool qff_validate_unicode_descriptor(qp_stream_t *stream, uint16_t num_unicode_glyphs) {
  69. // Read the raw descriptor
  70. qff_unicode_glyph_table_v1_t unicode_descriptor;
  71. if (qp_stream_read(&unicode_descriptor, sizeof(qff_unicode_glyph_table_v1_t), 1, stream) != 1) {
  72. qp_dprintf("Failed to read unicode_descriptor, expected length was not %d\n", (int)sizeof(qff_unicode_glyph_table_v1_t));
  73. return false;
  74. }
  75. // Make sure this block is valid
  76. if (!qgf_validate_block_header(&unicode_descriptor.header, QFF_UNICODE_GLYPH_DESCRIPTOR_TYPEID, num_unicode_glyphs * 6)) {
  77. return false;
  78. }
  79. // Skip the necessary amount of data to get to the next block
  80. qp_stream_seek(stream, num_unicode_glyphs * sizeof(qff_unicode_glyph_v1_t), SEEK_CUR);
  81. return true;
  82. }
  83. bool qff_validate_stream(qp_stream_t *stream) {
  84. bool has_ascii_table;
  85. uint16_t num_unicode_glyphs;
  86. if (!qff_read_font_descriptor(stream, NULL, &has_ascii_table, &num_unicode_glyphs, NULL, NULL, NULL, NULL)) {
  87. return false;
  88. }
  89. if (has_ascii_table) {
  90. if (!qff_validate_ascii_descriptor(stream)) {
  91. return false;
  92. }
  93. }
  94. if (num_unicode_glyphs > 0) {
  95. if (!qff_validate_unicode_descriptor(stream, num_unicode_glyphs)) {
  96. return false;
  97. }
  98. }
  99. return true;
  100. }
  101. uint32_t qff_get_total_size(qp_stream_t *stream) {
  102. // Get the original location
  103. uint32_t oldpos = qp_stream_tell(stream);
  104. // Read the font descriptor, grabbing the size
  105. uint32_t total_size;
  106. if (!qff_read_font_descriptor(stream, NULL, NULL, NULL, NULL, NULL, NULL, &total_size)) {
  107. return false;
  108. }
  109. // Restore the original location
  110. qp_stream_setpos(stream, oldpos);
  111. return total_size;
  112. }