mirror of
https://github.com/reactos/reactos.git
synced 2024-08-27 21:58:49 +00:00
551 lines
17 KiB
C
551 lines
17 KiB
C
/*
|
|
* COPYRIGHT: See COPYING.ARM in the top level directory
|
|
* PROJECT: ReactOS UEFI Boot Library
|
|
* FILE: boot/environ/lib/misc/resource.c
|
|
* PURPOSE: Boot Library Resource Functions
|
|
* PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include "bl.h"
|
|
|
|
/* DATA VARIABLES ************************************************************/
|
|
|
|
PVOID ResPeImageBase;
|
|
PVOID ResPeImageEnd;
|
|
PVOID ResRootDirectory;
|
|
|
|
PVOID ResPeImageBasePrimary;
|
|
PVOID ResPeImageEndPrimary;
|
|
PVOID ResRootDirectoryPrimary;
|
|
ULONG_PTR ResRootDirectoryPrimaryOffset;
|
|
ULONG_PTR ResRootDirectoryOffset;
|
|
ULONG_PTR ResRootDirectoryFallbackOffset;
|
|
PVOID ResPeImageBaseFallback;
|
|
PVOID ResPeImageEndFallback;
|
|
PVOID ResRootDirectoryFallback;
|
|
|
|
BOOLEAN ResLoadedFontFiles;
|
|
PVOID ResMuiImageBase;
|
|
ULONG_PTR ResMuiImageSize;
|
|
|
|
PWCHAR ResLocale;
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
NTSTATUS
|
|
ResSelectLocale (
|
|
_In_ BOOLEAN Primary
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
/* Check if we're using the primary (MUI) or fallback resources */
|
|
if (Primary)
|
|
{
|
|
/* Use the primary ones */
|
|
ResRootDirectory = ResRootDirectoryPrimary;
|
|
ResRootDirectoryOffset = ResRootDirectoryPrimaryOffset;
|
|
ResPeImageBase = ResPeImageBasePrimary;
|
|
ResPeImageEnd = ResPeImageEndPrimary;
|
|
|
|
/* Register the locale with the display */
|
|
Status = BlpDisplayRegisterLocale(ResLocale);
|
|
}
|
|
|
|
/* Check if that failed, or if we're using fallback */
|
|
if (!(Primary) || !(NT_SUCCESS(Status)))
|
|
{
|
|
/* Set the fallback pointers */
|
|
ResRootDirectory = ResRootDirectoryFallback;
|
|
ResRootDirectoryOffset = ResRootDirectoryFallbackOffset;
|
|
ResPeImageBase = ResPeImageBaseFallback;
|
|
ResPeImageEnd = ResPeImageEndFallback;
|
|
|
|
/* Register the fallback (America baby!) locale */
|
|
Status = BlpDisplayRegisterLocale(L"en-US");
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Fallback to text mode (yes, this is the API...) */
|
|
return BlDisplaySetScreenResolution();
|
|
}
|
|
}
|
|
|
|
/* No fonts loaded -- return failure code */
|
|
ResLoadedFontFiles = FALSE;
|
|
return Status;
|
|
}
|
|
|
|
PIMAGE_RESOURCE_DIRECTORY_ENTRY
|
|
ResFindDirectoryEntry (
|
|
_In_ PIMAGE_RESOURCE_DIRECTORY Directory,
|
|
_In_opt_ PUSHORT Id,
|
|
_In_opt_ PWCHAR Name,
|
|
_In_ ULONG_PTR SectionStart
|
|
)
|
|
{
|
|
PIMAGE_RESOURCE_DIRECTORY_ENTRY EntryTable, IdEntryTable;
|
|
ULONG i;
|
|
SIZE_T NameLength;
|
|
PIMAGE_RESOURCE_DIRECTORY_STRING NameString;
|
|
|
|
/* Are we looking by ID or name? */
|
|
if (Id)
|
|
{
|
|
/* By ID, so were we passed a name? */
|
|
if (Name)
|
|
{
|
|
/* That doesn't make sense */
|
|
return NULL;
|
|
}
|
|
}
|
|
else if (!Name)
|
|
{
|
|
/* By name, but we weren't given one. Also bad. */
|
|
return NULL;
|
|
}
|
|
|
|
/* Get the table of names */
|
|
EntryTable = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(Directory + 1);
|
|
|
|
/* Check if we are doing ID lookup instead */
|
|
if (Id)
|
|
{
|
|
/* The IDs come after the names */
|
|
IdEntryTable = &EntryTable[Directory->NumberOfNamedEntries];
|
|
|
|
/* Parse them */
|
|
for (i = 0; i < Directory->NumberOfIdEntries; i++)
|
|
{
|
|
/* Check if the ID matches, or if the wildcard is being used*/
|
|
if ((IdEntryTable[i].Id == *Id) || (*Id == 0xFFFF))
|
|
{
|
|
/* Return a pointer to the data */
|
|
return (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(SectionStart + IdEntryTable[i].OffsetToDirectory);
|
|
}
|
|
}
|
|
|
|
/* ID was not found */
|
|
return NULL;
|
|
}
|
|
|
|
/* Searching by name, so parse them */
|
|
for (i = 0; i < Directory->NumberOfNamedEntries; i++)
|
|
{
|
|
/* Get the name itself and count its length */
|
|
NameString = (PIMAGE_RESOURCE_DIRECTORY_STRING)(SectionStart + EntryTable[i].NameOffset);
|
|
NameLength = wcslen(Name);
|
|
|
|
/* If the length matches, compare the bytes */
|
|
if ((NameLength == NameString->Length) &&
|
|
(RtlCompareMemory(NameString->NameString, Name, NameLength) == NameLength))
|
|
{
|
|
/* They both match, so this is our entry. Return it */
|
|
return (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(SectionStart + EntryTable[i].OffsetToDirectory);
|
|
}
|
|
}
|
|
|
|
/* Name was not found */
|
|
return NULL;
|
|
}
|
|
|
|
NTSTATUS
|
|
ResFindDataEntryFromImage (
|
|
_In_opt_ PVOID ImageBase,
|
|
_In_opt_ ULONG ImageSize,
|
|
_In_ USHORT DirectoryId,
|
|
_In_ PUSHORT EntryId,
|
|
_In_ PWCHAR Name,
|
|
_Out_ PIMAGE_RESOURCE_DATA_ENTRY *DataEntryOut,
|
|
_Out_ PVOID* ResourceOut
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PIMAGE_SECTION_HEADER ResourceSection;
|
|
PIMAGE_RESOURCE_DIRECTORY ResourceDir, RootDir;
|
|
PIMAGE_RESOURCE_DIRECTORY_ENTRY DirEntry;
|
|
PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
|
|
PVOID Data, DataEnd, ImageEnd;
|
|
BOOLEAN UseFallbackDirectory;
|
|
|
|
/* Assume nothing found */
|
|
UseFallbackDirectory = TRUE;
|
|
Status = STATUS_NOT_FOUND;
|
|
|
|
/* Are we looking at a particular image? */
|
|
if (ImageBase)
|
|
{
|
|
/* Then make sure we know its size */
|
|
if (!ImageSize)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
/* Find the resource section for it */
|
|
ResourceSection = BlImgFindSection(ImageBase, ImageSize);
|
|
if (!ResourceSection)
|
|
{
|
|
return STATUS_INVALID_IMAGE_FORMAT;
|
|
}
|
|
|
|
/* Remember how big the image is, and find the resource directory */
|
|
ImageEnd = (PVOID)((ULONG_PTR)ImageBase + ImageSize);
|
|
RootDir = (PIMAGE_RESOURCE_DIRECTORY)((ULONG_PTR)ImageBase +
|
|
ResourceSection->VirtualAddress);
|
|
if ((PVOID)RootDir < ImageBase)
|
|
{
|
|
/* It's out of bounds, so bail out */
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* We have a valid directory, don't use fallback for now */
|
|
UseFallbackDirectory = FALSE;
|
|
}
|
|
else
|
|
{
|
|
/* We are using the current library settings instead */
|
|
ImageBase = ResPeImageBase;
|
|
RootDir = ResRootDirectory;
|
|
ImageEnd = ResPeImageEnd;
|
|
}
|
|
|
|
/* If we don't have a resource directory, there's nothing to find */
|
|
if (!RootDir)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
/* Try two loops, once for primary, once for fallback */
|
|
while (1)
|
|
{
|
|
/* Find the directory first */
|
|
ResourceDir = (PIMAGE_RESOURCE_DIRECTORY)ResFindDirectoryEntry(RootDir,
|
|
&DirectoryId,
|
|
NULL,
|
|
(ULONG_PTR)RootDir);
|
|
if (ResourceDir)
|
|
{
|
|
break;
|
|
}
|
|
|
|
/* We didn't find it -- is it time to use the fallback? */
|
|
if (UseFallbackDirectory)
|
|
{
|
|
/* Were were not using the fallback already? */
|
|
if (RootDir != ResRootDirectoryFallback)
|
|
{
|
|
/* Then attempt with the fallback instead*/
|
|
RootDir = ResRootDirectoryFallback;
|
|
ImageBase = ResPeImageBaseFallback;
|
|
ImageEnd = ResPeImageEndFallback;
|
|
|
|
/* Making sure we have one... */
|
|
if (RootDir)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Otherwise, return failure here */
|
|
return Status;
|
|
}
|
|
|
|
/* Now that we are in the right directory, lookup the resource */
|
|
ResourceDir = (PIMAGE_RESOURCE_DIRECTORY)ResFindDirectoryEntry(ResourceDir,
|
|
EntryId,
|
|
Name,
|
|
(ULONG_PTR)RootDir);
|
|
if (!ResourceDir)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
/* The entry is right after */
|
|
DirEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResourceDir + 1);
|
|
if ((PVOID)DirEntry < (PVOID)ResourceDir)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Get the data entry for it */
|
|
DataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)((ULONG_PTR)RootDir +
|
|
DirEntry->OffsetToData);
|
|
|
|
/* Check if the data entry is out of bounds */
|
|
if (((PVOID)DataEntry < ImageBase) || ((PVOID)DataEntry > ImageEnd))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Finally read the data offset */
|
|
Data = (PVOID)((ULONG_PTR)ImageBase + DataEntry->OffsetToData);
|
|
|
|
/* Check if the data is out of bounds */
|
|
if (((PVOID)Data < ImageBase) || ((PVOID)Data > ImageEnd))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Make sure the data end isn't out of bounds either */
|
|
DataEnd = (PVOID)((ULONG_PTR)Data + DataEntry->Size);
|
|
if (((PVOID)DataEnd < ImageBase) || ((PVOID)DataEnd > ImageEnd))
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* We finally made it. Return the entry and the raw data */
|
|
*DataEntryOut = DataEntry;
|
|
*ResourceOut = Data;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PWCHAR
|
|
BlResourceFindHtml (
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PIMAGE_RESOURCE_DATA_ENTRY HtmlDataEntry;
|
|
PWCHAR Stylesheet;
|
|
|
|
/* Assume failure */
|
|
Stylesheet = NULL;
|
|
|
|
/* Look for an RT_HTML resource called BOOTMGR.XSL */
|
|
Status = ResFindDataEntryFromImage(NULL,
|
|
0,
|
|
23,
|
|
NULL,
|
|
L"BOOTMGR.XSL",
|
|
&HtmlDataEntry,
|
|
(PVOID*)&Stylesheet);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Stylesheet;
|
|
}
|
|
|
|
/* Check for Unicode BOM */
|
|
if (*Stylesheet == 0xFEFF)
|
|
{
|
|
/* Overwrite it, and NULL-terminate */
|
|
RtlMoveMemory(Stylesheet,
|
|
Stylesheet + 1,
|
|
HtmlDataEntry->Size - sizeof(WCHAR));
|
|
Stylesheet[(HtmlDataEntry->Size / sizeof(WCHAR)) - 1] = UNICODE_NULL;
|
|
}
|
|
else if (Stylesheet[(HtmlDataEntry->Size / sizeof(WCHAR)) - 1] != UNICODE_NULL)
|
|
{
|
|
/* If it's not NULL-terminated, fail */
|
|
Stylesheet = NULL;
|
|
}
|
|
|
|
/* Return it back */
|
|
return Stylesheet;
|
|
}
|
|
|
|
PWCHAR
|
|
BlResourceFindMessage (
|
|
_In_ ULONG MsgId
|
|
)
|
|
{
|
|
PWCHAR Message;
|
|
PIMAGE_RESOURCE_DIRECTORY ResourceDir;
|
|
PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
|
|
PMESSAGE_RESOURCE_DATA MsgData;
|
|
PMESSAGE_RESOURCE_ENTRY MsgEntry;
|
|
ULONG i, j;
|
|
USHORT Id;
|
|
PVOID MsgEnd;
|
|
NTSTATUS Status;
|
|
|
|
/* Bail out if there's no resource directory */
|
|
Message = NULL;
|
|
if (!ResRootDirectory)
|
|
{
|
|
return Message;
|
|
}
|
|
|
|
/* Check if we've loaded fonts already */
|
|
if (!ResLoadedFontFiles)
|
|
{
|
|
/* Nope, load them now */
|
|
Status = BfLoadDeferredFontFiles();
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* We failed to load fonts, fallback to fallback locale */
|
|
Status = ResSelectLocale(FALSE);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Try fonts now */
|
|
Status = BfLoadDeferredFontFiles();
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* Still didn't work -- fallback to text mode */
|
|
EfiPrintf(L"Font loading failed, falling back to text mode\r\n");
|
|
Status = BlDisplaySetScreenResolution();
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
/* That didn't work either. F*ck it. */
|
|
return Message;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now we have a resource directory, and fonts are loaded */
|
|
NT_ASSERT(ResRootDirectory != NULL);
|
|
ResLoadedFontFiles = TRUE;
|
|
}
|
|
|
|
/* Go look for RT_MESSAGETABLE */
|
|
Id = 11;
|
|
ResourceDir = (PIMAGE_RESOURCE_DIRECTORY)ResFindDirectoryEntry(ResRootDirectory,
|
|
&Id,
|
|
NULL,
|
|
(ULONG_PTR)ResRootDirectory);
|
|
if (!ResourceDir)
|
|
{
|
|
return Message;
|
|
}
|
|
|
|
/* Go look for the first directory in the table */
|
|
Id = 1;
|
|
ResourceDir = (PIMAGE_RESOURCE_DIRECTORY)ResFindDirectoryEntry(ResourceDir,
|
|
&Id,
|
|
NULL,
|
|
(ULONG_PTR)ResRootDirectory);
|
|
if (!ResourceDir)
|
|
{
|
|
return Message;
|
|
}
|
|
|
|
/* Go look for any language entry in the table */
|
|
Id = -1;
|
|
DataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)ResFindDirectoryEntry(ResourceDir,
|
|
&Id,
|
|
NULL,
|
|
(ULONG_PTR)ResRootDirectory);
|
|
if (!DataEntry)
|
|
{
|
|
return Message;
|
|
}
|
|
|
|
/* Get the message data*/
|
|
MsgData = (PMESSAGE_RESOURCE_DATA)((ULONG_PTR)ResRootDirectory +
|
|
DataEntry->OffsetToData -
|
|
ResRootDirectoryOffset);
|
|
|
|
/* Loop through the message blocks */
|
|
for (j = 0; j < MsgData->NumberOfBlocks; j++)
|
|
{
|
|
/* Check if the ID is within this range */
|
|
if ((MsgId >= MsgData->Blocks[j].LowId) &&
|
|
(MsgId <= MsgData->Blocks[j].HighId))
|
|
{
|
|
/* Get the first entry */
|
|
MsgEntry = (PMESSAGE_RESOURCE_ENTRY)((ULONG_PTR)MsgData +
|
|
MsgData->Blocks[j].OffsetToEntries);
|
|
|
|
/* Loop till we find the right one */
|
|
for (i = MsgId - MsgData->Blocks[j].LowId; i; --i)
|
|
{
|
|
MsgEntry = (PMESSAGE_RESOURCE_ENTRY)((ULONG_PTR)MsgEntry +
|
|
MsgEntry->Length);
|
|
}
|
|
|
|
/* Find where this message ends */
|
|
MsgEnd = (PVOID)((ULONG_PTR)MsgEntry + MsgEntry->Length);
|
|
|
|
/* Now make sure that the message is within bounds */
|
|
if ((MsgEnd >= (PVOID)MsgEntry) &&
|
|
((PVOID)MsgEntry >= ResPeImageBase) &&
|
|
(MsgEnd <= ResPeImageEnd))
|
|
{
|
|
/* If so, read the text associated with it */
|
|
Message = (PWCHAR)MsgEntry->Text;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Return the text, if one was found */
|
|
return Message;
|
|
}
|
|
|
|
NTSTATUS
|
|
BlpResourceInitialize (
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PIMAGE_SECTION_HEADER ResourceSection;
|
|
PVOID ImageBase;
|
|
ULONG ImageSize, VRes, HRes;
|
|
BOOLEAN UsePrimary;
|
|
|
|
/* Default to using fallback */
|
|
UsePrimary = FALSE;
|
|
|
|
/* Initialize all globals */
|
|
ResMuiImageBase = 0;
|
|
ResMuiImageSize = 0;
|
|
ResRootDirectoryPrimary = 0;
|
|
ResRootDirectoryPrimaryOffset = 0;
|
|
ResPeImageBasePrimary = 0;
|
|
ResPeImageEndPrimary = 0;
|
|
ResRootDirectoryFallback = 0;
|
|
ResRootDirectoryFallbackOffset = 0;
|
|
ResPeImageBaseFallback = 0;
|
|
ResPeImageEndFallback = 0;
|
|
ResRootDirectory = 0;
|
|
ResRootDirectoryOffset = 0;
|
|
ResPeImageBase = 0;
|
|
ResPeImageEnd = 0;
|
|
ResLoadedFontFiles = 0;
|
|
|
|
/* Check if we had allocated a locale already */
|
|
if (ResLocale)
|
|
{
|
|
/* Free it and reset */
|
|
BlMmFreeHeap(ResLocale);
|
|
ResLocale = 0;
|
|
}
|
|
|
|
/* Get our base address and size*/
|
|
Status = BlGetApplicationBaseAndSize(&ImageBase, &ImageSize);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
/* Find our resource section */
|
|
ResourceSection = BlImgFindSection(ImageBase, ImageSize);
|
|
if (ResourceSection)
|
|
{
|
|
/* The resource section will be our fallback. Save down its details */
|
|
ResRootDirectoryFallbackOffset = ResourceSection->VirtualAddress;
|
|
ResPeImageBaseFallback = ImageBase;
|
|
ResPeImageEndFallback = (PVOID)((ULONG_PTR)ImageBase + ImageSize);
|
|
ResRootDirectoryFallback = (PIMAGE_RESOURCE_DIRECTORY)((ULONG_PTR)ImageBase +
|
|
ResRootDirectoryFallbackOffset);
|
|
}
|
|
|
|
/* Get the current screen resolution and check if we're in graphics mode */
|
|
Status = BlDisplayGetScreenResolution(&HRes, &VRes);
|
|
if ((NT_SUCCESS(Status)) && ((HRes != 640) || (VRes != 200)))
|
|
{
|
|
/* We are... we should load MUI data */
|
|
Status = STATUS_NOT_IMPLEMENTED;//ResInitializeMuiResources();
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* And not rely on the fallback */
|
|
UsePrimary = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Load the locale resources */
|
|
return ResSelectLocale(UsePrimary);
|
|
}
|