OpenCore  1.0.4
OpenCore Bootloader
Loading...
Searching...
No Matches
ConsoleFontLoader.c
Go to the documentation of this file.
1
9
10#include <Uefi.h>
11#include <Library/MemoryAllocationLib.h>
14
15#define HEX_LINE_DATA_OFFSET ((sizeof(CHAR16) * 2) + 1)
16#define HEX_LINE_LENGTH (HEX_LINE_DATA_OFFSET + (ISO_CHAR_HEIGHT * sizeof(UINT8) * 2))
17
18#define HEX_FILE_ERROR_PREFIX "OCC: Hex font line "
19
20//
21// Re-use exactly the structure we want, but with a UINTN in the pointer during
22// construction of the data. (Avoid making this a union, as it is not relevant
23// to the consumer of a font.)
24//
25#define PTR_AS_UINTN(ptr) (*((UINTN *) &ptr))
26
27//
28// Parse exactly Length upper or lower case hex digits with no prefix to UINTN.
29// Note: AsciiStrHexToUintn and friends allow prefix and surrounding space and
30// are auto-terminating (i.e. no specified length), none of which we want here.
31//
32STATIC
33EFI_STATUS
35 IN CHAR8 *Str,
36 OUT UINTN *Data,
37 IN UINTN Length
38 )
39{
40 UINT8 Char;
41
42 if (Data == NULL) {
43 return EFI_INVALID_PARAMETER;
44 }
45
46 if (Length > sizeof (UINTN) * 2) {
47 return EFI_UNSUPPORTED;
48 }
49
50 *Data = 0;
51 while (Length-- > 0) {
52 Char = (UINT8)(*Str++);
53 if ((Char >= '0') && (Char <= '9')) {
54 Char -= '0';
55 } else if ((Char >= 'A') && (Char <= 'F')) {
56 Char -= 'A';
57 Char += 10;
58 } else if ((Char >= 'a') && (Char <= 'f')) {
59 Char -= 'a';
60 Char += 10;
61 } else {
62 *Data = 0;
63 return EFI_INVALID_PARAMETER;
64 }
65
66 *Data = (*Data << 4) + Char;
67 }
68
69 return EFI_SUCCESS;
70}
71
72//
73// Line length already checked.
74//
75STATIC
76EFI_STATUS
78 UINTN LineNumber,
79 CHAR8 *CurrentLine,
80 CHAR16 *Char,
81 UINT8 *LineGlyphData
82 )
83{
84 EFI_STATUS Status;
85 UINTN Data;
86 UINT8 Line;
87
88 if (CurrentLine[4] != ':') {
89 DEBUG ((DEBUG_WARN, "%a%u illegal line\n", HEX_FILE_ERROR_PREFIX, LineNumber));
90 return EFI_UNSUPPORTED;
91 }
92
93 Status = AsciiHexToUintn (CurrentLine, &Data, 4);
94 if (EFI_ERROR (Status)) {
95 DEBUG ((DEBUG_WARN, "%a%u cannot parse character code\n", HEX_FILE_ERROR_PREFIX, LineNumber));
96 return EFI_UNSUPPORTED;
97 }
98
99 *Char = (CHAR16)Data;
100
101 for (Line = 0; Line < ISO_CHAR_HEIGHT; Line++) {
102 Status = AsciiHexToUintn (&CurrentLine[HEX_LINE_DATA_OFFSET + (Line * 2)], &Data, 2);
103 if (EFI_ERROR (Status)) {
104 DEBUG ((DEBUG_WARN, "%a%u cannot parse bitmap data\n", HEX_FILE_ERROR_PREFIX, LineNumber));
105 break;
106 }
107
108 LineGlyphData[Line] = (UINT8)Data;
109 }
110
111 return EFI_SUCCESS;
112}
113
114EFI_STATUS
116 IN OC_STORAGE_CONTEXT *Storage,
117 IN CONST CHAR8 *FontName,
118 OUT OC_CONSOLE_FONT **Font
119 )
120{
121 EFI_STATUS Status;
122 CHAR16 Path[OC_STORAGE_SAFE_PATH_MAX];
123 UINT8 Line;
124 UINTN Pass;
125 CHAR8 *HexFontFile;
126 CHAR8 *CurrentLine;
127 CHAR8 *NextLine;
128 CHAR8 *TrimmedLineEnd;
129 BOOLEAN Finished;
130 UINTN LineNumber;
131 OC_FLEX_ARRAY *FlexFontPages;
132 UINT16 FontPageMin;
133 UINT16 FontPageMax;
134 UINT16 Char;
135 UINT8 LineGlyphData[ISO_CHAR_HEIGHT];
136 UINT16 PageNumber;
137 UINT8 PageChar;
138 UINT16 CurrentPageNumber;
139 OC_CONSOLE_FONT_PAGE *FontPage;
140 UINT8 PageCharMin;
141 UINT8 PageCharMax;
142 UINT8 FontPageHead;
143 UINT8 FontPageTail;
144 UINT8 GlyphHead;
145 UINT8 GlyphTail;
146 UINT16 PageSparseCount;
147 UINT8 GlyphSparseCount;
148 BOOLEAN FoundNonZero;
149 UINT32 GlyphOffsetsSize;
150 UINT32 GlyphDataSize;
151 UINTN FontSize;
152 UINTN FontHeaderSize;
153 UINTN FontPagesSize;
154 UINTN FontPageOffsetsSize;
155 OC_CONSOLE_FONT_PAGE *FontPages;
156 UINT16 *FontPageOffsets;
157 UINT8 *GlyphOffsets;
158 UINT8 *GlyphData;
159 UINT16 *SavedFontPageOffsets;
160 UINT8 *SavedGlyphOffsets;
161 UINT8 *SavedGlyphData;
162 VOID *SavedEnd;
163
164 ASSERT (Font != NULL);
165 *Font = NULL;
166
167 Status = OcUnicodeSafeSPrint (
168 Path,
169 sizeof (Path),
170 OPEN_CORE_FONT_PATH L"\\%a.hex",
171 FontName
172 );
173 if (EFI_ERROR (Status)) {
174 DEBUG ((DEBUG_WARN, "OCC: Cannot fit %a\n", FontName));
175 return EFI_OUT_OF_RESOURCES;
176 }
177
178 HexFontFile = OcStorageReadFileUnicode (Storage, Path, NULL);
179 if (HexFontFile == NULL) {
180 return EFI_NOT_FOUND;
181 }
182
183 FlexFontPages = OcFlexArrayInit (sizeof (OC_CONSOLE_FONT_PAGE), NULL);
184
185 if (FlexFontPages == NULL) {
186 FreePool (HexFontFile);
187 return EFI_OUT_OF_RESOURCES;
188 }
189
190 for (Pass = 1; Pass <= 2; Pass++) {
191 FontPageMin = 0;
192 FontPageMax = 0;
193 CurrentPageNumber = MAX_UINT16;
194 PageSparseCount = 0;
195 GlyphDataSize = 0;
196 GlyphOffsetsSize = 0;
197
198 NextLine = HexFontFile;
199 LineNumber = 0;
200
201 while (TRUE) {
202 CurrentLine = NextLine;
203 ++LineNumber;
204
205 //
206 // Left trim.
207 //
208 while (*CurrentLine != '\n' && OcIsSpace (*CurrentLine)) {
209 ++CurrentLine;
210 }
211
212 Finished = (*CurrentLine == '\0');
213
214 if (!Finished) {
215 NextLine = AsciiStrStr (CurrentLine, "\n");
216 if (NextLine == NULL) {
217 NextLine = &CurrentLine[AsciiStrLen (CurrentLine)];
218 } else {
219 ++NextLine;
220 }
221
222 if (*CurrentLine == '#') {
223 continue;
224 }
225
226 //
227 // Right trim.
228 //
229 TrimmedLineEnd = NextLine - 1;
230 while (TrimmedLineEnd >= CurrentLine && OcIsSpace (*TrimmedLineEnd)) {
231 --TrimmedLineEnd;
232 }
233
234 ++TrimmedLineEnd;
235
236 if (TrimmedLineEnd == CurrentLine) {
237 continue;
238 }
239
240 if ((TrimmedLineEnd - CurrentLine) != HEX_LINE_LENGTH) {
241 DEBUG ((DEBUG_WARN, "%a%u illegal line length\n", HEX_FILE_ERROR_PREFIX, LineNumber));
242 Status = EFI_UNSUPPORTED;
243 break;
244 }
245
246 Status = ParseHexFontLine (LineNumber, CurrentLine, &Char, LineGlyphData);
247 if (EFI_ERROR (Status)) {
248 break;
249 }
250
251 PageNumber = (Char >> 7) & 0x1FF;
252 PageChar = Char & 0x7F;
253 }
254
255 if (Finished || (PageNumber != CurrentPageNumber)) {
256 //
257 // Output previous page.
258 //
259 if (CurrentPageNumber != MAX_UINT16) {
260 if (Pass == 1) {
261 FontPage = OcFlexArrayAddItem (FlexFontPages);
262 if (FontPage == NULL) {
263 Status = EFI_OUT_OF_RESOURCES;
264 break;
265 }
266
267 FontPage->CharMin = PageCharMin;
268 FontPage->CharMax = PageCharMax;
269 if (FontPageHead == ISO_CHAR_HEIGHT) {
270 ASSERT (FontPageTail == ISO_CHAR_HEIGHT);
271 FontPage->FontHead = ISO_CHAR_HEIGHT - (ISO_CHAR_HEIGHT / 2);
272 FontPage->FontTail = (ISO_CHAR_HEIGHT / 2);
273 } else {
274 FontPage->FontHead = FontPageHead;
275 FontPage->FontTail = FontPageTail;
276 }
277
278 FontPage->Glyphs = NULL;
279 PTR_AS_UINTN (FontPage->GlyphOffsets) = GlyphSparseCount;
280 FontPage->LeftToRight = TRUE;
281
282 GlyphDataSize += (ISO_CHAR_HEIGHT - FontPageHead - FontPageTail) * GlyphSparseCount;
283 if (GlyphSparseCount < PageCharMax - PageCharMin) {
284 GlyphOffsetsSize += (PageCharMax - PageCharMin);
285 }
286 } else {
287 if (GlyphSparseCount < PageCharMax - PageCharMin) {
288 GlyphOffsets += (PageCharMax - PageCharMin);
289 }
290 }
291 }
292
293 if (Finished) {
294 break;
295 }
296
297 CurrentPageNumber = PageNumber;
298
299 if (FontPageMax == 0) {
300 FontPageMin = CurrentPageNumber;
301 FontPageMax = CurrentPageNumber + 1;
302 } else if (CurrentPageNumber >= FontPageMax) {
303 FontPageMax = CurrentPageNumber + 1;
304 } else {
305 DEBUG ((DEBUG_WARN, "%a%u hex file must be sorted #%u\n", HEX_FILE_ERROR_PREFIX, LineNumber, 1));
306 Status = EFI_UNSUPPORTED;
307 break;
308 }
309
310 ++PageSparseCount;
311
312 PageCharMin = PageChar;
313 if (Pass == 1) {
314 FontPageHead = ISO_CHAR_HEIGHT;
315 FontPageTail = ISO_CHAR_HEIGHT;
316 } else {
317 FontPage = FontPages;
318 ++FontPages;
319
320 ASSERT (FontPage->CharMin == PageCharMin);
321 ASSERT (FontPage->CharMax > FontPage->CharMin);
322 FontPageHead = FontPage->FontHead;
323 FontPageTail = FontPage->FontTail;
324
325 if (FontPageOffsets != NULL) {
326 ASSERT ((UINT8 *)&FontPageOffsets[CurrentPageNumber - FontPageMin] < SavedGlyphOffsets);
327 FontPageOffsets[CurrentPageNumber - FontPageMin] = PageSparseCount;
328 }
329
330 FontPage->Glyphs = GlyphData;
331 //
332 // Only use GlyphOffsets table when chars in page are not continuous.
333 //
334 FontPage->GlyphOffsets =
335 ((INTN)PTR_AS_UINTN (FontPage->GlyphOffsets) == (FontPage->CharMax - FontPage->CharMin))
336 ? NULL
337 : GlyphOffsets;
338 }
339
340 PageCharMax = PageCharMin + 1;
341 GlyphSparseCount = 0;
342 } else if (PageChar >= PageCharMax) {
343 PageCharMax = PageChar + 1;
344 } else {
345 DEBUG ((DEBUG_WARN, "%a%u hex file must be sorted #%u\n", HEX_FILE_ERROR_PREFIX, LineNumber, 2));
346 Status = EFI_UNSUPPORTED;
347 break;
348 }
349
350 ++GlyphSparseCount;
351
352 if (Pass == 1) {
353 GlyphHead = ISO_CHAR_HEIGHT;
354 GlyphTail = ISO_CHAR_HEIGHT;
355 FoundNonZero = FALSE;
356 for (Line = 0; Line < ISO_CHAR_HEIGHT; Line++) {
357 if (LineGlyphData[Line] != 0) {
358 if (!FoundNonZero) {
359 GlyphHead = Line;
360 FoundNonZero = TRUE;
361 }
362
363 GlyphTail = ISO_CHAR_HEIGHT - Line - 1;
364 }
365 }
366
367 if (GlyphHead < FontPageHead) {
368 FontPageHead = GlyphHead;
369 }
370
371 if (GlyphTail < FontPageTail) {
372 FontPageTail = GlyphTail;
373 }
374 } else {
375 if (FontPage->GlyphOffsets != NULL) {
376 FontPage->GlyphOffsets[PageChar - PageCharMin] = GlyphSparseCount;
377 }
378
379 CopyMem (GlyphData, &LineGlyphData[FontPageHead], ISO_CHAR_HEIGHT - FontPageHead - FontPageTail);
380 GlyphData += ISO_CHAR_HEIGHT - FontPageHead - FontPageTail;
381 }
382 }
383
384 ASSERT ((FlexFontPages != NULL) == (Pass == 1));
385 ASSERT ((*Font != NULL) == (Pass == 2));
386
387 if (EFI_ERROR (Status)) {
388 ASSERT (Pass == 1);
389 break;
390 }
391
392 if (Pass == 1) {
393 FontHeaderSize = sizeof (OC_CONSOLE_FONT);
394 FontPagesSize = PageSparseCount * sizeof (OC_CONSOLE_FONT_PAGE);
395 FontPageOffsetsSize =
396 ((FontPageMax - FontPageMin) == PageSparseCount)
397 ? 0
398 : ((FontPageMax - FontPageMin) * sizeof (UINT16));
399
400 FontSize =
401 FontHeaderSize +
402 FontPagesSize +
403 FontPageOffsetsSize +
404 GlyphOffsetsSize +
405 GlyphDataSize;
406
407 //
408 // Arranged in decreasing alignment requirement, so no need for padding.
409 //
410 *Font = AllocateZeroPool (FontSize);
411 if (*Font == NULL) {
412 OcFlexArrayFree (&FlexFontPages);
413 return EFI_OUT_OF_RESOURCES;
414 }
415
416 DEBUG ((DEBUG_INFO, "OCC: Hex font allocated size %u\n", FontSize));
417
418 FontPages = (VOID *)((UINT8 *)(*Font) + FontHeaderSize);
419 SavedFontPageOffsets = FontPageOffsets = (VOID *)((UINT8 *)FontPages + FontPagesSize);
420 SavedGlyphOffsets = GlyphOffsets = (VOID *)((UINT8 *)FontPageOffsets + FontPageOffsetsSize);
421 SavedGlyphData = GlyphData = (VOID *)((UINT8 *)GlyphOffsets + GlyphOffsetsSize);
422 SavedEnd = (UINT8 *)GlyphData + GlyphDataSize;
423
424 if (FontPageOffsetsSize == 0) {
425 FontPageOffsets = NULL;
426 }
427
428 if (GlyphOffsetsSize == 0) {
429 GlyphOffsets = NULL;
430 }
431
432 (*Font)->PageMin = FontPageMin;
433 (*Font)->PageMax = FontPageMax;
434 (*Font)->Pages = FontPages;
435 (*Font)->PageOffsets = FontPageOffsets;
436
437 //
438 // Move flex array items to their permanent home.
439 //
440 ASSERT (FontPagesSize == FlexFontPages->ItemSize * FlexFontPages->Count);
441 CopyMem (FontPages, FlexFontPages->Items, FlexFontPages->ItemSize * FlexFontPages->Count);
442
443 OcFlexArrayFree (&FlexFontPages);
444 } else {
445 ASSERT ((UINT8 *)FontPages == (UINT8 *)SavedFontPageOffsets);
446 ASSERT (GlyphOffsets == NULL ? (UINT8 *)SavedGlyphOffsets == (UINT8 *)SavedFontPageOffsets : (UINT8 *)GlyphOffsets == (UINT8 *)SavedGlyphData);
447 ASSERT (GlyphData == SavedEnd);
448 }
449 }
450
451 FreePool (HexFontFile);
452
453 if (!EFI_ERROR (Status)) {
455 Status = EFI_UNSUPPORTED;
456 DEBUG ((DEBUG_WARN, "OCC: Hex font does not contain fallback character '%c' - %r\n", OC_CONSOLE_FONT_FALLBACK_CHAR, Status));
457 }
458 }
459
460 if (EFI_ERROR (Status)) {
461 if (FlexFontPages != NULL) {
462 OcFlexArrayFree (&FlexFontPages);
463 }
464
465 if (*Font != NULL) {
466 FreePool (*Font);
467 }
468 }
469
470 return Status;
471}
UINT64 Length
#define HEX_LINE_LENGTH
EFI_STATUS OcLoadConsoleFont(IN OC_STORAGE_CONTEXT *Storage, IN CONST CHAR8 *FontName, OUT OC_CONSOLE_FONT **Font)
#define HEX_FILE_ERROR_PREFIX
STATIC EFI_STATUS AsciiHexToUintn(IN CHAR8 *Str, OUT UINTN *Data, IN UINTN Length)
#define HEX_LINE_DATA_OFFSET
STATIC EFI_STATUS ParseHexFontLine(UINTN LineNumber, CHAR8 *CurrentLine, CHAR16 *Char, UINT8 *LineGlyphData)
#define PTR_AS_UINTN(ptr)
#define OPEN_CORE_FONT_PATH
#define OC_CONSOLE_FONT_FALLBACK_CHAR
struct _OC_CONSOLE_FONT_PAGE OC_CONSOLE_FONT_PAGE
struct _OC_CONSOLE_FONT OC_CONSOLE_FONT
#define ISO_CHAR_HEIGHT
BOOLEAN OcConsoleFontContainsChar(IN OC_CONSOLE_FONT *ConsoleFont, IN CHAR16 Char)
VOID OcFlexArrayFree(IN OUT OC_FLEX_ARRAY **FlexArray)
OC_FLEX_ARRAY * OcFlexArrayInit(IN CONST UINTN ItemSize, IN CONST OC_FLEX_ARRAY_FREE_ITEM FreeItem OPTIONAL)
Definition FlexArray.c:31
VOID * OcFlexArrayAddItem(IN OUT OC_FLEX_ARRAY *FlexArray)
Definition FlexArray.c:136
VOID * OcStorageReadFileUnicode(IN OC_STORAGE_CONTEXT *Context, IN CONST CHAR16 *FilePath, OUT UINT32 *FileSize OPTIONAL)
#define OC_STORAGE_SAFE_PATH_MAX
BOOLEAN OcIsSpace(CHAR16 Ch)
EFI_STATUS EFIAPI OcUnicodeSafeSPrint(OUT CHAR16 *StartOfBuffer, IN UINTN BufferSize, IN CONST CHAR16 *FormatString,...)
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
#define ASSERT(x)
Definition coder.h:55