mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
OcLoadConsoleFont currently loads the specified console path from Resources\Font\\, which can cause vault failures if vault.plist doesn't include the duplicate path separator. Remove it to match other paths.
472 lines
14 KiB
C
472 lines
14 KiB
C
/** @file
|
|
Load console font from standard format .hex font file - currently supports 8x16 fonts only.
|
|
|
|
Copyright (c) 2023, Mike Beaton. All rights reserved.<BR>
|
|
SPDX-License-Identifier: BSD-3-Clause
|
|
**/
|
|
|
|
#include "OcConsoleLibInternal.h"
|
|
|
|
#include <Uefi.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/OcFlexArrayLib.h>
|
|
#include <Library/OcStorageLib.h>
|
|
|
|
#define HEX_LINE_DATA_OFFSET ((sizeof(CHAR16) * 2) + 1)
|
|
#define HEX_LINE_LENGTH (HEX_LINE_DATA_OFFSET + (ISO_CHAR_HEIGHT * sizeof(UINT8) * 2))
|
|
|
|
#define HEX_FILE_ERROR_PREFIX "OCC: Hex font line "
|
|
|
|
//
|
|
// Re-use exactly the structure we want, but with a UINTN in the pointer during
|
|
// construction of the data. (Avoid making this a union, as it is not relevant
|
|
// to the consumer of a font.)
|
|
//
|
|
#define PTR_AS_UINTN(ptr) (*((UINTN *) &ptr))
|
|
|
|
//
|
|
// Parse exactly Length upper or lower case hex digits with no prefix to UINTN.
|
|
// Note: AsciiStrHexToUintn and friends allow prefix and surrounding space and
|
|
// are auto-terminating (i.e. no specified length), none of which we want here.
|
|
//
|
|
STATIC
|
|
EFI_STATUS
|
|
AsciiHexToUintn (
|
|
IN CHAR8 *Str,
|
|
OUT UINTN *Data,
|
|
IN UINTN Length
|
|
)
|
|
{
|
|
UINT8 Char;
|
|
|
|
if (Data == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (Length > sizeof (UINTN) * 2) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
*Data = 0;
|
|
while (Length-- > 0) {
|
|
Char = (UINT8)(*Str++);
|
|
if ((Char >= '0') && (Char <= '9')) {
|
|
Char -= '0';
|
|
} else if ((Char >= 'A') && (Char <= 'F')) {
|
|
Char -= 'A';
|
|
Char += 10;
|
|
} else if ((Char >= 'a') && (Char <= 'f')) {
|
|
Char -= 'a';
|
|
Char += 10;
|
|
} else {
|
|
*Data = 0;
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
*Data = (*Data << 4) + Char;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Line length already checked.
|
|
//
|
|
STATIC
|
|
EFI_STATUS
|
|
ParseHexFontLine (
|
|
UINTN LineNumber,
|
|
CHAR8 *CurrentLine,
|
|
CHAR16 *Char,
|
|
UINT8 *LineGlyphData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Data;
|
|
UINT8 Line;
|
|
|
|
if (CurrentLine[4] != ':') {
|
|
DEBUG ((DEBUG_WARN, "%a%u illegal line\n", HEX_FILE_ERROR_PREFIX, LineNumber));
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
Status = AsciiHexToUintn (CurrentLine, &Data, 4);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_WARN, "%a%u cannot parse character code\n", HEX_FILE_ERROR_PREFIX, LineNumber));
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
*Char = (CHAR16)Data;
|
|
|
|
for (Line = 0; Line < ISO_CHAR_HEIGHT; Line++) {
|
|
Status = AsciiHexToUintn (&CurrentLine[HEX_LINE_DATA_OFFSET + (Line * 2)], &Data, 2);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_WARN, "%a%u cannot parse bitmap data\n", HEX_FILE_ERROR_PREFIX, LineNumber));
|
|
break;
|
|
}
|
|
|
|
LineGlyphData[Line] = (UINT8)Data;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
OcLoadConsoleFont (
|
|
IN OC_STORAGE_CONTEXT *Storage,
|
|
IN CONST CHAR8 *FontName,
|
|
OUT OC_CONSOLE_FONT **Font
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR16 Path[OC_STORAGE_SAFE_PATH_MAX];
|
|
UINT8 Line;
|
|
UINTN Pass;
|
|
CHAR8 *HexFontFile;
|
|
CHAR8 *CurrentLine;
|
|
CHAR8 *NextLine;
|
|
CHAR8 *TrimmedLineEnd;
|
|
BOOLEAN Finished;
|
|
UINTN LineNumber;
|
|
OC_FLEX_ARRAY *FlexFontPages;
|
|
UINT16 FontPageMin;
|
|
UINT16 FontPageMax;
|
|
UINT16 Char;
|
|
UINT8 LineGlyphData[ISO_CHAR_HEIGHT];
|
|
UINT16 PageNumber;
|
|
UINT8 PageChar;
|
|
UINT16 CurrentPageNumber;
|
|
OC_CONSOLE_FONT_PAGE *FontPage;
|
|
UINT8 PageCharMin;
|
|
UINT8 PageCharMax;
|
|
UINT8 FontPageHead;
|
|
UINT8 FontPageTail;
|
|
UINT8 GlyphHead;
|
|
UINT8 GlyphTail;
|
|
UINT16 PageSparseCount;
|
|
UINT8 GlyphSparseCount;
|
|
BOOLEAN FoundNonZero;
|
|
UINT32 GlyphOffsetsSize;
|
|
UINT32 GlyphDataSize;
|
|
UINTN FontSize;
|
|
UINTN FontHeaderSize;
|
|
UINTN FontPagesSize;
|
|
UINTN FontPageOffsetsSize;
|
|
OC_CONSOLE_FONT_PAGE *FontPages;
|
|
UINT16 *FontPageOffsets;
|
|
UINT8 *GlyphOffsets;
|
|
UINT8 *GlyphData;
|
|
UINT16 *SavedFontPageOffsets;
|
|
UINT8 *SavedGlyphOffsets;
|
|
UINT8 *SavedGlyphData;
|
|
VOID *SavedEnd;
|
|
|
|
ASSERT (Font != NULL);
|
|
*Font = NULL;
|
|
|
|
Status = OcUnicodeSafeSPrint (
|
|
Path,
|
|
sizeof (Path),
|
|
OPEN_CORE_FONT_PATH L"%a.hex",
|
|
FontName
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
DEBUG ((DEBUG_WARN, "OCC: Cannot fit %a\n", FontName));
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
HexFontFile = OcStorageReadFileUnicode (Storage, Path, NULL);
|
|
if (HexFontFile == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
FlexFontPages = OcFlexArrayInit (sizeof (OC_CONSOLE_FONT_PAGE), NULL);
|
|
|
|
if (FlexFontPages == NULL) {
|
|
FreePool (HexFontFile);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
for (Pass = 1; Pass <= 2; Pass++) {
|
|
FontPageMin = 0;
|
|
FontPageMax = 0;
|
|
CurrentPageNumber = MAX_UINT16;
|
|
PageSparseCount = 0;
|
|
GlyphDataSize = 0;
|
|
GlyphOffsetsSize = 0;
|
|
|
|
NextLine = HexFontFile;
|
|
LineNumber = 0;
|
|
|
|
while (TRUE) {
|
|
CurrentLine = NextLine;
|
|
++LineNumber;
|
|
|
|
//
|
|
// Left trim.
|
|
//
|
|
while (*CurrentLine != '\n' && OcIsSpace (*CurrentLine)) {
|
|
++CurrentLine;
|
|
}
|
|
|
|
Finished = (*CurrentLine == '\0');
|
|
|
|
if (!Finished) {
|
|
NextLine = AsciiStrStr (CurrentLine, "\n");
|
|
if (NextLine == NULL) {
|
|
NextLine = &CurrentLine[AsciiStrLen (CurrentLine)];
|
|
} else {
|
|
++NextLine;
|
|
}
|
|
|
|
if (*CurrentLine == '#') {
|
|
continue; ///< Comment, on separate line only.
|
|
}
|
|
|
|
//
|
|
// Right trim.
|
|
//
|
|
TrimmedLineEnd = NextLine - 1;
|
|
while (TrimmedLineEnd >= CurrentLine && OcIsSpace (*TrimmedLineEnd)) {
|
|
--TrimmedLineEnd;
|
|
}
|
|
|
|
++TrimmedLineEnd;
|
|
|
|
if (TrimmedLineEnd == CurrentLine) {
|
|
continue; ///< Empty line.
|
|
}
|
|
|
|
if ((TrimmedLineEnd - CurrentLine) != HEX_LINE_LENGTH) {
|
|
DEBUG ((DEBUG_WARN, "%a%u illegal line length\n", HEX_FILE_ERROR_PREFIX, LineNumber));
|
|
Status = EFI_UNSUPPORTED;
|
|
break;
|
|
}
|
|
|
|
Status = ParseHexFontLine (LineNumber, CurrentLine, &Char, LineGlyphData);
|
|
if (EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
|
|
PageNumber = (Char >> 7) & 0x1FF;
|
|
PageChar = Char & 0x7F;
|
|
}
|
|
|
|
if (Finished || (PageNumber != CurrentPageNumber)) {
|
|
//
|
|
// Output previous page.
|
|
//
|
|
if (CurrentPageNumber != MAX_UINT16) {
|
|
if (Pass == 1) {
|
|
FontPage = OcFlexArrayAddItem (FlexFontPages);
|
|
if (FontPage == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
break;
|
|
}
|
|
|
|
FontPage->CharMin = PageCharMin;
|
|
FontPage->CharMax = PageCharMax;
|
|
if (FontPageHead == ISO_CHAR_HEIGHT) {
|
|
ASSERT (FontPageTail == ISO_CHAR_HEIGHT);
|
|
FontPage->FontHead = ISO_CHAR_HEIGHT - (ISO_CHAR_HEIGHT / 2);
|
|
FontPage->FontTail = (ISO_CHAR_HEIGHT / 2);
|
|
} else {
|
|
FontPage->FontHead = FontPageHead;
|
|
FontPage->FontTail = FontPageTail;
|
|
}
|
|
|
|
FontPage->Glyphs = NULL;
|
|
PTR_AS_UINTN (FontPage->GlyphOffsets) = GlyphSparseCount;
|
|
FontPage->LeftToRight = TRUE;
|
|
|
|
GlyphDataSize += (ISO_CHAR_HEIGHT - FontPageHead - FontPageTail) * GlyphSparseCount;
|
|
if (GlyphSparseCount < PageCharMax - PageCharMin) {
|
|
GlyphOffsetsSize += (PageCharMax - PageCharMin);
|
|
}
|
|
} else {
|
|
if (GlyphSparseCount < PageCharMax - PageCharMin) {
|
|
GlyphOffsets += (PageCharMax - PageCharMin);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Finished) {
|
|
break;
|
|
}
|
|
|
|
CurrentPageNumber = PageNumber;
|
|
|
|
if (FontPageMax == 0) {
|
|
FontPageMin = CurrentPageNumber;
|
|
FontPageMax = CurrentPageNumber + 1;
|
|
} else if (CurrentPageNumber >= FontPageMax) {
|
|
FontPageMax = CurrentPageNumber + 1;
|
|
} else {
|
|
DEBUG ((DEBUG_WARN, "%a%u hex file must be sorted #%u\n", HEX_FILE_ERROR_PREFIX, LineNumber, 1));
|
|
Status = EFI_UNSUPPORTED;
|
|
break;
|
|
}
|
|
|
|
++PageSparseCount;
|
|
|
|
PageCharMin = PageChar;
|
|
if (Pass == 1) {
|
|
FontPageHead = ISO_CHAR_HEIGHT;
|
|
FontPageTail = ISO_CHAR_HEIGHT;
|
|
} else {
|
|
FontPage = FontPages;
|
|
++FontPages;
|
|
|
|
ASSERT (FontPage->CharMin == PageCharMin);
|
|
ASSERT (FontPage->CharMax > FontPage->CharMin);
|
|
FontPageHead = FontPage->FontHead;
|
|
FontPageTail = FontPage->FontTail;
|
|
|
|
if (FontPageOffsets != NULL) {
|
|
ASSERT ((UINT8 *)&FontPageOffsets[CurrentPageNumber - FontPageMin] < SavedGlyphOffsets);
|
|
FontPageOffsets[CurrentPageNumber - FontPageMin] = PageSparseCount; ///< stores sparse index + 1; zeroes mean no page.
|
|
}
|
|
|
|
FontPage->Glyphs = GlyphData;
|
|
//
|
|
// Only use GlyphOffsets table when chars in page are not continuous.
|
|
//
|
|
FontPage->GlyphOffsets =
|
|
((INTN)PTR_AS_UINTN (FontPage->GlyphOffsets) == (FontPage->CharMax - FontPage->CharMin))
|
|
? NULL
|
|
: GlyphOffsets;
|
|
}
|
|
|
|
PageCharMax = PageCharMin + 1;
|
|
GlyphSparseCount = 0;
|
|
} else if (PageChar >= PageCharMax) {
|
|
PageCharMax = PageChar + 1;
|
|
} else {
|
|
DEBUG ((DEBUG_WARN, "%a%u hex file must be sorted #%u\n", HEX_FILE_ERROR_PREFIX, LineNumber, 2));
|
|
Status = EFI_UNSUPPORTED;
|
|
break;
|
|
}
|
|
|
|
++GlyphSparseCount;
|
|
|
|
if (Pass == 1) {
|
|
GlyphHead = ISO_CHAR_HEIGHT;
|
|
GlyphTail = ISO_CHAR_HEIGHT;
|
|
FoundNonZero = FALSE;
|
|
for (Line = 0; Line < ISO_CHAR_HEIGHT; Line++) {
|
|
if (LineGlyphData[Line] != 0) {
|
|
if (!FoundNonZero) {
|
|
GlyphHead = Line;
|
|
FoundNonZero = TRUE;
|
|
}
|
|
|
|
GlyphTail = ISO_CHAR_HEIGHT - Line - 1;
|
|
}
|
|
}
|
|
|
|
if (GlyphHead < FontPageHead) {
|
|
FontPageHead = GlyphHead;
|
|
}
|
|
|
|
if (GlyphTail < FontPageTail) {
|
|
FontPageTail = GlyphTail;
|
|
}
|
|
} else {
|
|
if (FontPage->GlyphOffsets != NULL) {
|
|
FontPage->GlyphOffsets[PageChar - PageCharMin] = GlyphSparseCount; ///< stores sparse index + 1; zeroes mean no glyph.
|
|
}
|
|
|
|
CopyMem (GlyphData, &LineGlyphData[FontPageHead], ISO_CHAR_HEIGHT - FontPageHead - FontPageTail);
|
|
GlyphData += ISO_CHAR_HEIGHT - FontPageHead - FontPageTail;
|
|
}
|
|
}
|
|
|
|
ASSERT ((FlexFontPages != NULL) == (Pass == 1));
|
|
ASSERT ((*Font != NULL) == (Pass == 2));
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT (Pass == 1);
|
|
break;
|
|
}
|
|
|
|
if (Pass == 1) {
|
|
FontHeaderSize = sizeof (OC_CONSOLE_FONT);
|
|
FontPagesSize = PageSparseCount * sizeof (OC_CONSOLE_FONT_PAGE);
|
|
FontPageOffsetsSize =
|
|
((FontPageMax - FontPageMin) == PageSparseCount)
|
|
? 0
|
|
: ((FontPageMax - FontPageMin) * sizeof (UINT16));
|
|
|
|
FontSize =
|
|
FontHeaderSize +
|
|
FontPagesSize +
|
|
FontPageOffsetsSize +
|
|
GlyphOffsetsSize +
|
|
GlyphDataSize;
|
|
|
|
//
|
|
// Arranged in decreasing alignment requirement, so no need for padding.
|
|
//
|
|
*Font = AllocateZeroPool (FontSize);
|
|
if (*Font == NULL) {
|
|
OcFlexArrayFree (&FlexFontPages);
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
DEBUG ((DEBUG_INFO, "OCC: Hex font allocated size %u\n", FontSize));
|
|
|
|
FontPages = (VOID *)((UINT8 *)(*Font) + FontHeaderSize);
|
|
SavedFontPageOffsets = FontPageOffsets = (VOID *)((UINT8 *)FontPages + FontPagesSize);
|
|
SavedGlyphOffsets = GlyphOffsets = (VOID *)((UINT8 *)FontPageOffsets + FontPageOffsetsSize);
|
|
SavedGlyphData = GlyphData = (VOID *)((UINT8 *)GlyphOffsets + GlyphOffsetsSize);
|
|
SavedEnd = (UINT8 *)GlyphData + GlyphDataSize;
|
|
|
|
if (FontPageOffsetsSize == 0) {
|
|
FontPageOffsets = NULL;
|
|
}
|
|
|
|
if (GlyphOffsetsSize == 0) {
|
|
GlyphOffsets = NULL;
|
|
}
|
|
|
|
(*Font)->PageMin = FontPageMin;
|
|
(*Font)->PageMax = FontPageMax;
|
|
(*Font)->Pages = FontPages;
|
|
(*Font)->PageOffsets = FontPageOffsets;
|
|
|
|
//
|
|
// Move flex array items to their permanent home.
|
|
//
|
|
ASSERT (FontPagesSize == FlexFontPages->ItemSize * FlexFontPages->Count);
|
|
CopyMem (FontPages, FlexFontPages->Items, FlexFontPages->ItemSize * FlexFontPages->Count);
|
|
|
|
OcFlexArrayFree (&FlexFontPages);
|
|
} else {
|
|
ASSERT ((UINT8 *)FontPages == (UINT8 *)SavedFontPageOffsets);
|
|
ASSERT (GlyphOffsets == NULL ? (UINT8 *)SavedGlyphOffsets == (UINT8 *)SavedFontPageOffsets : (UINT8 *)GlyphOffsets == (UINT8 *)SavedGlyphData);
|
|
ASSERT (GlyphData == SavedEnd);
|
|
}
|
|
}
|
|
|
|
FreePool (HexFontFile);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
if (!OcConsoleFontContainsChar (*Font, OC_CONSOLE_FONT_FALLBACK_CHAR)) {
|
|
Status = EFI_UNSUPPORTED;
|
|
DEBUG ((DEBUG_WARN, "OCC: Hex font does not contain fallback character '%c' - %r\n", OC_CONSOLE_FONT_FALLBACK_CHAR, Status));
|
|
}
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
if (FlexFontPages != NULL) {
|
|
OcFlexArrayFree (&FlexFontPages);
|
|
}
|
|
|
|
if (*Font != NULL) {
|
|
FreePool (*Font);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|