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