xref: /reactos/boot/environ/lib/misc/resource.c (revision 9393fc32)
1 /*
2  * COPYRIGHT:       See COPYING.ARM in the top level directory
3  * PROJECT:         ReactOS UEFI Boot Library
4  * FILE:            boot/environ/lib/misc/resource.c
5  * PURPOSE:         Boot Library Resource Functions
6  * PROGRAMMER:      Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include "bl.h"
12 
13 /* DATA VARIABLES ************************************************************/
14 
15 PVOID ResPeImageBase;
16 PVOID ResPeImageEnd;
17 PVOID ResRootDirectory;
18 
19 PVOID ResPeImageBasePrimary;
20 PVOID ResPeImageEndPrimary;
21 PVOID ResRootDirectoryPrimary;
22 ULONG_PTR ResRootDirectoryPrimaryOffset;
23 ULONG_PTR ResRootDirectoryOffset;
24 ULONG_PTR ResRootDirectoryFallbackOffset;
25 PVOID ResPeImageBaseFallback;
26 PVOID ResPeImageEndFallback;
27 PVOID ResRootDirectoryFallback;
28 
29 BOOLEAN ResLoadedFontFiles;
30 PVOID ResMuiImageBase;
31 ULONG_PTR ResMuiImageSize;
32 
33 PWCHAR ResLocale;
34 
35 /* FUNCTIONS *****************************************************************/
36 
37 NTSTATUS
ResSelectLocale(_In_ BOOLEAN Primary)38 ResSelectLocale (
39     _In_ BOOLEAN Primary
40     )
41 {
42     NTSTATUS Status;
43 
44     /* Check if we're using the primary (MUI) or fallback resources */
45     if (Primary)
46     {
47         /* Use the primary ones */
48         ResRootDirectory = ResRootDirectoryPrimary;
49         ResRootDirectoryOffset = ResRootDirectoryPrimaryOffset;
50         ResPeImageBase = ResPeImageBasePrimary;
51         ResPeImageEnd = ResPeImageEndPrimary;
52 
53         /* Register the locale with the display */
54         Status = BlpDisplayRegisterLocale(ResLocale);
55     }
56 
57     /* Check if that failed, or if we're using fallback */
58     if (!(Primary) || !(NT_SUCCESS(Status)))
59     {
60         /* Set the fallback pointers */
61         ResRootDirectory = ResRootDirectoryFallback;
62         ResRootDirectoryOffset = ResRootDirectoryFallbackOffset;
63         ResPeImageBase = ResPeImageBaseFallback;
64         ResPeImageEnd = ResPeImageEndFallback;
65 
66         /* Register the fallback (America baby!) locale */
67         Status = BlpDisplayRegisterLocale(L"en-US");
68         if (!NT_SUCCESS(Status))
69         {
70             /* Fallback to text mode (yes, this is the API...) */
71             return BlDisplaySetScreenResolution();
72         }
73     }
74 
75     /* No fonts loaded -- return failure code */
76     ResLoadedFontFiles = FALSE;
77     return Status;
78 }
79 
80 PIMAGE_RESOURCE_DIRECTORY_ENTRY
ResFindDirectoryEntry(_In_ PIMAGE_RESOURCE_DIRECTORY Directory,_In_opt_ PUSHORT Id,_In_opt_ PWCHAR Name,_In_ ULONG_PTR SectionStart)81 ResFindDirectoryEntry (
82     _In_ PIMAGE_RESOURCE_DIRECTORY Directory,
83     _In_opt_ PUSHORT Id,
84     _In_opt_ PWCHAR Name,
85     _In_ ULONG_PTR SectionStart
86     )
87 {
88     PIMAGE_RESOURCE_DIRECTORY_ENTRY EntryTable, IdEntryTable;
89     ULONG i;
90     SIZE_T NameLength;
91     PIMAGE_RESOURCE_DIRECTORY_STRING NameString;
92 
93     /* Are we looking by ID or name? */
94     if (Id)
95     {
96         /* By ID, so were we passed a name? */
97         if (Name)
98         {
99             /* That doesn't make sense */
100             return NULL;
101         }
102     }
103     else if (!Name)
104     {
105         /* By name, but we weren't given one. Also bad. */
106         return NULL;
107     }
108 
109     /* Get the table of names */
110     EntryTable = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(Directory + 1);
111 
112     /* Check if we are doing ID lookup instead */
113     if (Id)
114     {
115         /* The IDs come after the names */
116         IdEntryTable = &EntryTable[Directory->NumberOfNamedEntries];
117 
118         /* Parse them */
119         for (i = 0; i < Directory->NumberOfIdEntries; i++)
120         {
121             /* Check if the ID matches, or if the wildcard is being used*/
122             if ((IdEntryTable[i].Id == *Id) || (*Id == 0xFFFF))
123             {
124                 /* Return a pointer to the data */
125                 return (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(SectionStart + IdEntryTable[i].OffsetToDirectory);
126             }
127         }
128 
129         /* ID was not found */
130         return NULL;
131     }
132 
133     /* Searching by name, so parse them */
134     for (i = 0; i < Directory->NumberOfNamedEntries; i++)
135     {
136         /* Get the name itself and count its length */
137         NameString = (PIMAGE_RESOURCE_DIRECTORY_STRING)(SectionStart + EntryTable[i].NameOffset);
138         NameLength = wcslen(Name);
139 
140         /* If the length matches, compare the bytes */
141         if ((NameLength == NameString->Length) &&
142             (RtlCompareMemory(NameString->NameString, Name, NameLength) == NameLength))
143         {
144             /* They both match, so this is our entry. Return it */
145             return (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(SectionStart + EntryTable[i].OffsetToDirectory);
146         }
147     }
148 
149     /* Name was not found */
150     return NULL;
151 }
152 
153 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)154 ResFindDataEntryFromImage (
155     _In_opt_ PVOID ImageBase,
156     _In_opt_ ULONG ImageSize,
157     _In_ USHORT DirectoryId,
158     _In_ PUSHORT EntryId,
159     _In_ PWCHAR Name,
160     _Out_ PIMAGE_RESOURCE_DATA_ENTRY *DataEntryOut,
161     _Out_ PVOID* ResourceOut
162     )
163 {
164     NTSTATUS Status;
165     PIMAGE_SECTION_HEADER ResourceSection;
166     PIMAGE_RESOURCE_DIRECTORY ResourceDir, RootDir;
167     PIMAGE_RESOURCE_DIRECTORY_ENTRY DirEntry;
168     PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
169     PVOID Data, DataEnd, ImageEnd;
170     BOOLEAN UseFallbackDirectory;
171 
172     /* Assume nothing found */
173     UseFallbackDirectory = TRUE;
174     Status = STATUS_NOT_FOUND;
175 
176     /* Are we looking at a particular image? */
177     if (ImageBase)
178     {
179         /* Then make sure we know its size */
180         if (!ImageSize)
181         {
182             return Status;
183         }
184 
185         /* Find the resource section for it */
186         ResourceSection = BlImgFindSection(ImageBase, ImageSize);
187         if (!ResourceSection)
188         {
189             return STATUS_INVALID_IMAGE_FORMAT;
190         }
191 
192         /* Remember how big the image is, and find the resource directory */
193         ImageEnd = (PVOID)((ULONG_PTR)ImageBase + ImageSize);
194         RootDir = (PIMAGE_RESOURCE_DIRECTORY)((ULONG_PTR)ImageBase +
195                                               ResourceSection->VirtualAddress);
196         if ((PVOID)RootDir < ImageBase)
197         {
198             /* It's out of bounds, so bail out */
199             return STATUS_INVALID_PARAMETER;
200         }
201 
202         /* We have a valid directory, don't use fallback for now */
203         UseFallbackDirectory = FALSE;
204     }
205     else
206     {
207         /* We are using the current library settings instead */
208         ImageBase = ResPeImageBase;
209         RootDir = ResRootDirectory;
210         ImageEnd = ResPeImageEnd;
211     }
212 
213     /* If we don't have a resource directory, there's nothing to find */
214     if (!RootDir)
215     {
216         return Status;
217     }
218 
219     /* Try two loops, once for primary, once for fallback */
220     while (1)
221     {
222         /* Find the directory first */
223         ResourceDir = (PIMAGE_RESOURCE_DIRECTORY)ResFindDirectoryEntry(RootDir,
224                                                                        &DirectoryId,
225                                                                        NULL,
226                                                                        (ULONG_PTR)RootDir);
227         if (ResourceDir)
228         {
229             break;
230         }
231 
232         /* We didn't find it -- is it time to use the fallback? */
233         if (UseFallbackDirectory)
234         {
235             /* Were were not using the fallback already? */
236             if (RootDir != ResRootDirectoryFallback)
237             {
238                 /* Then attempt with the fallback instead*/
239                 RootDir = ResRootDirectoryFallback;
240                 ImageBase = ResPeImageBaseFallback;
241                 ImageEnd = ResPeImageEndFallback;
242 
243                 /* Making sure we have one... */
244                 if (RootDir)
245                 {
246                     continue;
247                 }
248             }
249         }
250 
251         /* Otherwise, return failure here */
252         return Status;
253     }
254 
255     /* Now that we are in the right directory, lookup the resource */
256     ResourceDir = (PIMAGE_RESOURCE_DIRECTORY)ResFindDirectoryEntry(ResourceDir,
257                                                                    EntryId,
258                                                                    Name,
259                                                                    (ULONG_PTR)RootDir);
260     if (!ResourceDir)
261     {
262         return Status;
263     }
264 
265     /* The entry is right after */
266     DirEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResourceDir + 1);
267     if ((PVOID)DirEntry < (PVOID)ResourceDir)
268     {
269         return STATUS_INVALID_PARAMETER;
270     }
271 
272     /* Get the data entry for it */
273     DataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)((ULONG_PTR)RootDir +
274                                              DirEntry->OffsetToData);
275 
276     /* Check if the data entry is out of bounds */
277     if (((PVOID)DataEntry < ImageBase) || ((PVOID)DataEntry > ImageEnd))
278     {
279         return STATUS_INVALID_PARAMETER;
280     }
281 
282     /* Finally read the data offset */
283     Data = (PVOID)((ULONG_PTR)ImageBase + DataEntry->OffsetToData);
284 
285     /* Check if the data is out of bounds */
286     if (((PVOID)Data < ImageBase) || ((PVOID)Data > ImageEnd))
287     {
288         return STATUS_INVALID_PARAMETER;
289     }
290 
291     /* Make sure the data end isn't out of bounds either */
292     DataEnd = (PVOID)((ULONG_PTR)Data + DataEntry->Size);
293     if (((PVOID)DataEnd < ImageBase) || ((PVOID)DataEnd > ImageEnd))
294     {
295         return STATUS_INVALID_PARAMETER;
296     }
297 
298     /* We finally made it. Return the entry and the raw data */
299     *DataEntryOut = DataEntry;
300     *ResourceOut = Data;
301     return STATUS_SUCCESS;
302 }
303 
304 PWCHAR
BlResourceFindHtml(VOID)305 BlResourceFindHtml (
306     VOID
307     )
308 {
309     NTSTATUS Status;
310     PIMAGE_RESOURCE_DATA_ENTRY HtmlDataEntry;
311     PWCHAR Stylesheet;
312 
313     /* Assume failure */
314     Stylesheet = NULL;
315 
316     /* Look for an RT_HTML resource called BOOTMGR.XSL */
317     Status = ResFindDataEntryFromImage(NULL,
318                                        0,
319                                        23,
320                                        NULL,
321                                        L"BOOTMGR.XSL",
322                                        &HtmlDataEntry,
323                                        (PVOID*)&Stylesheet);
324     if (!NT_SUCCESS(Status))
325     {
326         return Stylesheet;
327     }
328 
329     /* Check for Unicode BOM */
330     if (*Stylesheet == 0xFEFF)
331     {
332         /* Overwrite it, and NULL-terminate */
333         RtlMoveMemory(Stylesheet,
334                       Stylesheet + 1,
335                       HtmlDataEntry->Size - sizeof(WCHAR));
336         Stylesheet[(HtmlDataEntry->Size / sizeof(WCHAR)) - 1] = UNICODE_NULL;
337     }
338     else if (Stylesheet[(HtmlDataEntry->Size / sizeof(WCHAR)) - 1] != UNICODE_NULL)
339     {
340         /* If it's not NULL-terminated, fail */
341         Stylesheet = NULL;
342     }
343 
344     /* Return it back */
345     return Stylesheet;
346 }
347 
348 PWCHAR
BlResourceFindMessage(_In_ ULONG MsgId)349 BlResourceFindMessage (
350     _In_ ULONG MsgId
351     )
352 {
353     PWCHAR Message;
354     PIMAGE_RESOURCE_DIRECTORY ResourceDir;
355     PIMAGE_RESOURCE_DATA_ENTRY DataEntry;
356     PMESSAGE_RESOURCE_DATA MsgData;
357     PMESSAGE_RESOURCE_ENTRY MsgEntry;
358     ULONG i, j;
359     USHORT Id;
360     PVOID MsgEnd;
361     NTSTATUS Status;
362 
363     /* Bail out if there's no resource directory */
364     Message = NULL;
365     if (!ResRootDirectory)
366     {
367         return Message;
368     }
369 
370     /* Check if we've loaded fonts already */
371     if (!ResLoadedFontFiles)
372     {
373         /* Nope, load them now */
374         Status = BfLoadDeferredFontFiles();
375         if (!NT_SUCCESS(Status))
376         {
377             /* We failed to load fonts, fallback to fallback locale */
378             Status = ResSelectLocale(FALSE);
379             if (NT_SUCCESS(Status))
380             {
381                 /* Try fonts now */
382                 Status = BfLoadDeferredFontFiles();
383                 if (!NT_SUCCESS(Status))
384                 {
385                     /* Still didn't work -- fallback to text mode */
386                     EfiPrintf(L"Font loading failed, falling back to text mode\r\n");
387                     Status = BlDisplaySetScreenResolution();
388                     if (!NT_SUCCESS(Status))
389                     {
390                         /* That didn't work either. F*ck it. */
391                         return Message;
392                     }
393                 }
394             }
395         }
396 
397         /* Now we have a resource directory, and fonts are loaded */
398         NT_ASSERT(ResRootDirectory != NULL);
399         ResLoadedFontFiles = TRUE;
400     }
401 
402     /* Go look for RT_MESSAGETABLE */
403     Id = 11;
404     ResourceDir = (PIMAGE_RESOURCE_DIRECTORY)ResFindDirectoryEntry(ResRootDirectory,
405                                                                    &Id,
406                                                                    NULL,
407                                                                    (ULONG_PTR)ResRootDirectory);
408     if (!ResourceDir)
409     {
410         return Message;
411     }
412 
413     /* Go look for the first directory in the table */
414     Id = 1;
415     ResourceDir = (PIMAGE_RESOURCE_DIRECTORY)ResFindDirectoryEntry(ResourceDir,
416                                                                    &Id,
417                                                                    NULL,
418                                                                    (ULONG_PTR)ResRootDirectory);
419     if (!ResourceDir)
420     {
421         return Message;
422     }
423 
424     /* Go look for any language entry in the table */
425     Id = -1;
426     DataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)ResFindDirectoryEntry(ResourceDir,
427                                                                   &Id,
428                                                                   NULL,
429                                                                   (ULONG_PTR)ResRootDirectory);
430     if (!DataEntry)
431     {
432         return Message;
433     }
434 
435     /* Get the message data*/
436     MsgData = (PMESSAGE_RESOURCE_DATA)((ULONG_PTR)ResRootDirectory +
437                                         DataEntry->OffsetToData -
438                                         ResRootDirectoryOffset);
439 
440     /* Loop through the message blocks */
441     for (j = 0; j < MsgData->NumberOfBlocks; j++)
442     {
443         /* Check if the ID is within this range */
444         if ((MsgId >= MsgData->Blocks[j].LowId) &&
445             (MsgId <= MsgData->Blocks[j].HighId))
446         {
447             /* Get the first entry */
448             MsgEntry = (PMESSAGE_RESOURCE_ENTRY)((ULONG_PTR)MsgData +
449                                                  MsgData->Blocks[j].OffsetToEntries);
450 
451             /* Loop till we find the right one */
452             for (i = MsgId - MsgData->Blocks[j].LowId; i; --i)
453             {
454                 MsgEntry = (PMESSAGE_RESOURCE_ENTRY)((ULONG_PTR)MsgEntry +
455                                                      MsgEntry->Length);
456             }
457 
458             /* Find where this message ends */
459             MsgEnd = (PVOID)((ULONG_PTR)MsgEntry + MsgEntry->Length);
460 
461             /* Now make sure that the message is within bounds */
462             if ((MsgEnd >= (PVOID)MsgEntry) &&
463                 ((PVOID)MsgEntry >= ResPeImageBase) &&
464                 (MsgEnd <= ResPeImageEnd))
465             {
466                 /* If so, read the text associated with it */
467                 Message = (PWCHAR)MsgEntry->Text;
468                 break;
469             }
470         }
471     }
472 
473     /* Return the text, if one was found */
474     return Message;
475 }
476 
477 NTSTATUS
BlpResourceInitialize(VOID)478 BlpResourceInitialize (
479     VOID
480     )
481 {
482     NTSTATUS Status;
483     PIMAGE_SECTION_HEADER ResourceSection;
484     PVOID ImageBase;
485     ULONG ImageSize, VRes, HRes;
486     BOOLEAN UsePrimary;
487 
488     /* Default to using fallback */
489     UsePrimary = FALSE;
490 
491     /* Initialize all globals */
492     ResMuiImageBase = 0;
493     ResMuiImageSize = 0;
494     ResRootDirectoryPrimary = 0;
495     ResRootDirectoryPrimaryOffset = 0;
496     ResPeImageBasePrimary = 0;
497     ResPeImageEndPrimary = 0;
498     ResRootDirectoryFallback = 0;
499     ResRootDirectoryFallbackOffset = 0;
500     ResPeImageBaseFallback = 0;
501     ResPeImageEndFallback = 0;
502     ResRootDirectory = 0;
503     ResRootDirectoryOffset = 0;
504     ResPeImageBase = 0;
505     ResPeImageEnd = 0;
506     ResLoadedFontFiles = 0;
507 
508     /* Check if we had allocated a locale already */
509     if (ResLocale)
510     {
511         /* Free it and reset */
512         BlMmFreeHeap(ResLocale);
513         ResLocale = 0;
514     }
515 
516     /* Get our base address and size*/
517     Status = BlGetApplicationBaseAndSize(&ImageBase, &ImageSize);
518     if (!NT_SUCCESS(Status))
519     {
520         return Status;
521     }
522 
523     /* Find our resource section */
524     ResourceSection = BlImgFindSection(ImageBase, ImageSize);
525     if (ResourceSection)
526     {
527         /* The resource section will be our fallback. Save down its details */
528         ResRootDirectoryFallbackOffset = ResourceSection->VirtualAddress;
529         ResPeImageBaseFallback = ImageBase;
530         ResPeImageEndFallback = (PVOID)((ULONG_PTR)ImageBase + ImageSize);
531         ResRootDirectoryFallback = (PIMAGE_RESOURCE_DIRECTORY)((ULONG_PTR)ImageBase +
532                                             ResRootDirectoryFallbackOffset);
533     }
534 
535     /* Get the current screen resolution and check if we're in graphics mode */
536     Status = BlDisplayGetScreenResolution(&HRes, &VRes);
537     if ((NT_SUCCESS(Status)) && ((HRes != 640) || (VRes != 200)))
538     {
539         /* We are... we should load MUI data */
540         Status = STATUS_NOT_IMPLEMENTED;//ResInitializeMuiResources();
541         if (NT_SUCCESS(Status))
542         {
543             /* And not rely on the fallback */
544             UsePrimary = TRUE;
545         }
546     }
547 
548     /* Load the locale resources */
549     return ResSelectLocale(UsePrimary);
550 }
551