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 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 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 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 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 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 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