reactos/boot/environ/lib/misc/resource.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);
}