Annotation of xom/lib/jlaefi.c, revision 1.1.1.1

1.1       root        1: /*++
                      2: 
                      3: Copyright (c) 2006 JLA
                      4: 
                      5: Module Name:
                      6: 
                      7:     jlaefi.c
                      8: 
                      9: Abstract:
                     10: 
                     11:     EFI helper routines
                     12: 
                     13: Revision History
                     14: 
                     15: --*/
                     16: 
                     17: #include "efi.h"
                     18: #include "efilib.h"
                     19: #include "../lib/jlaefi.h"
                     20: 
                     21: EFI_HANDLE imageHandle;
                     22: EFI_LOADED_IMAGE* pLoadedImage;
                     23: struct _EFI_CONSOLE_CONTROL_PROTOCOL* pConsoleControl;
                     24: SIMPLE_INPUT_INTERFACE* pKeyboard;
                     25: EFI_UGA_DRAW_PROTOCOL* pUgaDraw = 0;
                     26: EFI_DECOMPRESS_PROTOCOL* pDecompress;
                     27: 
                     28: UINTN argc;
                     29: CHAR16** argv;
                     30: EFI_STATUS status;
                     31: 
                     32: #define SHELL_INTERFACE_PROTOCOL \
                     33:   { 0x47c7b223, 0xc42a, 0x11d2, 0x8e, 0x57, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b }
                     34: 
                     35: EFI_GUID ShellInterfaceProtocol = SHELL_INTERFACE_PROTOCOL;
                     36: 
                     37: typedef struct _EFI_SHELL_INTERFACE {
                     38:   // Handle back to original image handle & image info
                     39:   EFI_HANDLE                  ImageHandle;
                     40:   EFI_LOADED_IMAGE            *Info;
                     41: 
                     42:   // Parsed arg list
                     43:   CHAR16                      **Argv;
                     44:   UINTN                       Argc;
                     45: 
                     46:   // Storage for file redirection args after parsing
                     47:   CHAR16                      **RedirArgv;
                     48:   UINTN                       RedirArgc;
                     49: 
                     50:   // A file style handle for console io
                     51:   EFI_FILE_HANDLE             StdIn;
                     52:   EFI_FILE_HANDLE             StdOut;
                     53:   EFI_FILE_HANDLE             StdErr;
                     54: 
                     55: } EFI_SHELL_INTERFACE;
                     56: 
                     57: EFI_GUID ConsoleControlProtocol = { 0xF42F7782, 0x012E, 0x4C12, 0x99, 0x56, 0x49, 0xf9, 0x43, 0x04, 0xf7, 0x21 };
                     58: 
                     59: typedef enum {
                     60:   EfiConsoleControlScreenText,
                     61:   EfiConsoleControlScreenGraphics,
                     62:   EfiConsoleControlScreenMax,
                     63: } EFI_CONSOLE_CONTROL_SCREEN_MODE;
                     64: 
                     65: typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL {
                     66:   void (*GetMode)(struct _EFI_CONSOLE_CONTROL_PROTOCOL* pThis, EFI_CONSOLE_CONTROL_SCREEN_MODE* currentMode, UINT32 n0, UINT32 n1);
                     67:   void (*SetMode)(struct _EFI_CONSOLE_CONTROL_PROTOCOL* pThis, EFI_CONSOLE_CONTROL_SCREEN_MODE  currentMode);
                     68: } EFI_CONSOLE_CONTROL_PROTOCOL;
                     69: 
                     70: void InitJLAEFI(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE* SystemTable) {
                     71:   //
                     72:   // Initialize the Library.
                     73:   //
                     74:   InitializeLib (ImageHandle, SystemTable);
                     75:   pKeyboard = SystemTable->ConIn;
                     76:   imageHandle = ImageHandle;
                     77: 
                     78:   // Get loaded image interface to implement exit() function
                     79:   status = BS->HandleProtocol(imageHandle, &LoadedImageProtocol, &pLoadedImage);
                     80:   if (EFI_ERROR(status)) {
                     81:     Print(L"Unable to obtain LoadedImageProtocol interface: %r\n", status);
                     82:     exit(EFI_SUCCESS);
                     83:   }
                     84: 
                     85:   // Get console control interface to set text mode
                     86:   status = LibLocateProtocol(&ConsoleControlProtocol, &pConsoleControl);
                     87:   if (EFI_ERROR(status)) {
                     88:     Print(L"Unable to obtain ConsoleControl interface: %r\n", status);
                     89:     exit(EFI_SUCCESS);
                     90:   }
                     91: 
                     92:   // Get arg list
                     93:   {
                     94:     EFI_SHELL_INTERFACE* pShell;
                     95:     UINT32 handleCount, i;
                     96:     EFI_HANDLE* handles;
                     97: 
                     98:     LibLocateHandle(ByProtocol, &ShellInterfaceProtocol, NULL, &handleCount, &handles);
                     99:     for (i = 0; i < handleCount; ++i) {
                    100:       BS->HandleProtocol(handles[i], &ShellInterfaceProtocol, &pShell);
                    101:       if (pShell->Argc)
                    102:         break;
                    103:     }
                    104: 
                    105:     argc = pShell->Argc;
                    106:     argv = pShell->Argv;
                    107:   }
                    108: 
                    109:   // Locate UGA draw
                    110:   // My iMac 17" reports that two handles implement UgaDraw
                    111:   // But only one actually draws on the screen!!
                    112:   // Both report the same resolution, both implement TextOut.
                    113:   // And worse... Both TextOut->OutputString print to the screen!! (???)
                    114:   // So... in order to draw to the screen, which one should I choose?
                    115:   // What has worked so far is using the second one returned (HACK!)
                    116:   {
                    117:     UINTN NoHandles;
                    118:     UINTN Index;
                    119:     EFI_HANDLE* Handles;
                    120: 
                    121:     // Get list of UgaDraw implementors
                    122:     LibLocateHandle(ByProtocol, &UgaDrawProtocol, NULL, &NoHandles, &Handles);
                    123: 
                    124:     // Enumerate all but keep only the last one (HACK!)
                    125:     for (Index = 0; Index < NoHandles; Index++)
                    126:       BS->HandleProtocol(Handles[Index], &UgaDrawProtocol, &pUgaDraw);
                    127: 
                    128:     // Sanity
                    129:     if (!pUgaDraw)
                    130:       Print(L"jlaefi: Unable to find UgaDraw interface\n");
                    131:   }
                    132: 
                    133:   // Locate decompress algorithm
                    134:   status = LibLocateProtocol(&DecompressProtocol, &pDecompress);
                    135:   if (EFI_ERROR(status)) {
                    136:     Print(L"Unable to obtain Decompress interface: %r\n", status);
                    137:     exit(EFI_SUCCESS);
                    138:   }
                    139: }
                    140: 
                    141: void consoleControl(UINTN graphicsMode) {
                    142:   if (pConsoleControl)
                    143:     pConsoleControl->SetMode(pConsoleControl, graphicsMode ? EfiConsoleControlScreenGraphics : EfiConsoleControlScreenText);
                    144: }
                    145: 
                    146: EFI_STATUS exec(EFI_HANDLE device, CHAR16* filename) {
                    147:   EFI_DEVICE_PATH* filePath;
                    148:   EFI_HANDLE handle;
                    149: 
                    150:   if (device == NULL)
                    151:     device = pLoadedImage ? pLoadedImage->DeviceHandle : NULL;
                    152: 
                    153:   if (device == NULL) {
                    154:     Print(L"exec(%s): Invalid device\n", filename);
                    155:     return EFI_INVALID_PARAMETER;
                    156:   }
                    157: 
                    158:   filePath = FileDevicePath(device, filename);
                    159:   if (!filePath) {
                    160:     Print(L"exec(%s): Unable to create device path\n", filename);
                    161:     return EFI_OUT_OF_RESOURCES;
                    162:   }
                    163: 
                    164:   status = BS->LoadImage(FALSE, imageHandle, filePath, NULL, 0, &handle);
                    165:   if (EFI_ERROR(status)) {
                    166:     Print(L"exec(%s): Unable to load image: %r\n", filename, status);
                    167:     return EFI_NOT_FOUND;
                    168:   }
                    169: 
                    170:   status = BS->StartImage(handle, NULL, NULL);
                    171:   if (EFI_ERROR(status)) {
                    172:     Print(L"exec(%s): Unable to start image: %r\n", filename, status);
                    173:     return EFI_NOT_STARTED;
                    174:   }
                    175:   return EFI_SUCCESS;
                    176: }
                    177: 
                    178: void exit(EFI_STATUS status) {
                    179:   consoleControl(0);
                    180:   BS->Exit(imageHandle, status, 0, NULL);
                    181: }
                    182: 
                    183: CHAR16 inputChar(CHAR16* prompt, CHAR16 dflt) {
                    184:   CHAR16 buffer[16];
                    185: 
                    186:   Print(L"%s [%c] : ", prompt, dflt);
                    187:   Input(NULL, buffer, sizeof(buffer));
                    188:   Print(L"\n");
                    189:   if (buffer[0] == 'q')
                    190:     exit(EFI_SUCCESS);
                    191:   if (!buffer[0])
                    192:     return dflt;
                    193:   return buffer[0];
                    194: }
                    195: 
                    196: UINT32 inputHex(CHAR16* prompt, UINT32 dflt) {
                    197:   CHAR16 buffer[16];
                    198: 
                    199:   Print(L"%s [%04x] : ", prompt, dflt);
                    200:   Input(NULL, buffer, sizeof(buffer));
                    201:   Print(L"\n");
                    202:   if (buffer[0] == 'q')
                    203:     exit(EFI_SUCCESS);
                    204:   if (!buffer[0])
                    205:     return dflt;
                    206:   return xtoi(buffer);
                    207: }
                    208: 
                    209: UINT32 inputDec(CHAR16* prompt, UINT32 dflt) {
                    210:   CHAR16 buffer[16];
                    211: 
                    212:   Print(L"%s [%4d] : ", prompt, dflt);
                    213:   Input(NULL, buffer, sizeof(buffer));
                    214:   Print(L"\n");
                    215:   if (buffer[0] == 'q')
                    216:     exit(EFI_SUCCESS);
                    217:   if (!buffer[0])
                    218:     return dflt;
                    219:   return Atoi(buffer);
                    220: }
                    221: 
                    222: void saveMemory(EFI_FILE* rootDir, UINT32 address, UINT32 length, UINTN copyFirst) {
                    223: 
                    224: #define COPYBUFFERSIZE 0x100000
                    225: 
                    226:   EFI_STATUS status;
                    227:   EFI_FILE* datFile;
                    228:   CHAR16 filename[16];
                    229:   UINT32 sz = length;
                    230:   UINT8* copyBuffer;
                    231: 
                    232:   SPrint(filename, sizeof(filename), L"%05x.dat", address);
                    233:   status = rootDir->Open(rootDir, &datFile, filename, EFI_FILE_MODE_READ, 0);
                    234:   if (!EFI_ERROR(status)) {
                    235:     datFile->Delete(datFile);
                    236:     datFile->Close(datFile);
                    237:   }
                    238: 
                    239:   status = rootDir->Open(rootDir, &datFile, filename, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
                    240:   if (EFI_ERROR(status)) {
                    241:     Print(L"Open(%s): %r\n", filename, status);
                    242:     exit(status);
                    243:   }
                    244: 
                    245:   if (copyFirst) {
                    246:     copyBuffer = AllocatePool(COPYBUFFERSIZE);
                    247:     if (!copyBuffer) {
                    248:       Print(L"Unable to allocate %x byte copy buffer\n", COPYBUFFERSIZE);
                    249:       exit(EFI_OUT_OF_RESOURCES);
                    250:     }
                    251:   }
                    252: 
                    253:   while (length) {
                    254:     UINT8* srcPtr;
                    255:     UINT32 sz;
                    256: 
                    257:     if (copyFirst) {
                    258:       sz = length > COPYBUFFERSIZE ? COPYBUFFERSIZE : length;
                    259:       __asm mov edi, copyBuffer
                    260:       __asm mov esi, address
                    261:       __asm mov ecx, sz
                    262:       __asm shr ecx, 2
                    263:       __asm rep movsd
                    264:       address += sz;
                    265:       srcPtr = copyBuffer;
                    266:       length -= sz;
                    267:     }
                    268:     else {
                    269:       sz = length;
                    270:       srcPtr = (UINT8*)address;
                    271:       length = 0;
                    272:     }
                    273:     Print(L"%X %X %X %X\n", srcPtr, sz, address, length);
                    274: 
                    275:     status = datFile->Write(datFile, &sz, srcPtr);
                    276:     if (EFI_ERROR(status)) {
                    277:       Print(L"Write(%s): %r\n", filename, status);
                    278:       exit(status);
                    279:     }
                    280:   }
                    281: 
                    282:   if (copyFirst) {
                    283:     FreePool(copyBuffer);
                    284:   }
                    285: 
                    286:   Print(L"%s, %d bytes\n", filename, length);
                    287: 
                    288:   datFile->Close(datFile);
                    289: 
                    290: }
                    291: 
                    292: EFI_FILE* fileOpen(EFI_HANDLE device, CHAR16* filename, UINTN writeable) {
                    293:   EFI_FILE* root;
                    294:   EFI_FILE* file;
                    295: 
                    296:   if (device == NULL)
                    297:     device = pLoadedImage ? pLoadedImage->DeviceHandle : NULL;
                    298: 
                    299:   if (device == NULL) {
                    300:     Print(L"Invalid device\n");
                    301:     return NULL;
                    302:   }
                    303: 
                    304:   root = LibOpenRoot(device);
                    305:   if (root == NULL)
                    306:     return root;
                    307: 
                    308:   status = root->Open(root, &file, filename, EFI_FILE_MODE_READ, 0);
                    309:   if (writeable) {
                    310:     if (!EFI_ERROR(status)) {
                    311:       file->Delete(file);
                    312:       file->Close(file);
                    313:     }
                    314: 
                    315:     status = root->Open(root, &file, filename, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, 0);
                    316:   }
                    317: 
                    318:   if (EFI_ERROR(status))
                    319:     Print(L"Open(%s): %r\n", filename, status);
                    320:   root->Close(root);
                    321: 
                    322:   return file;
                    323: }
                    324: 
                    325: EFI_FILE* getFileSystem() {
                    326:   UINT32 i, j;
                    327:   UINTN handleCount;
                    328:   EFI_HANDLE* handles;
                    329:   EFI_HANDLE fs[64];
                    330:   EFI_FILE* fileSystem;
                    331: 
                    332:   // Display all potential storage devices
                    333:   LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &handleCount, &handles);
                    334: 
                    335:   for (i = 0, j = 0; i < handleCount && j < sizeof(fs) / sizeof(EFI_HANDLE); ++i) {
                    336:     EFI_HANDLE handle = handles[i];
                    337:     EFI_DEVICE_PATH* dpath = DevicePathFromHandle(handle);
                    338: 
                    339:     if (!dpath)
                    340:       continue;
                    341: 
                    342:     Print(L"%x. %s\n", j + 1, DevicePathToStr(dpath));
                    343:     fs[j++] = handle;
                    344:   }
                    345:   if (!j) {
                    346:     Print(L"No filesystems available\n");
                    347:     exit(EFI_UNSUPPORTED);
                    348:   }
                    349: 
                    350:   // Get the user to choose the filesystem
                    351:   do {
                    352:     UINT32 i = inputHex(L"Please select the target filesystem ", 1);
                    353:   } while (i < 1 || i >= j + 1);
                    354: 
                    355:   // Open file system
                    356:   fileSystem = LibOpenRoot(fs[i - 1]);
                    357:   if (!fileSystem) {
                    358:     Print(L"Unable to open volume\n");
                    359:     exit(EFI_DEVICE_ERROR);
                    360:   }
                    361: 
                    362:   return fileSystem;
                    363: }
                    364: 
                    365: EFI_HANDLE* handleList;
                    366: UINTN maxHandles;
                    367: 
                    368: void initHandleList() {
                    369:   if (!handleList) {
                    370:     LibLocateHandle(AllHandles, NULL, NULL, &maxHandles, &handleList);
                    371:   }
                    372: }
                    373: 
                    374: EFI_HANDLE getHandleById(UINTN id) {
                    375:   initHandleList();
                    376: 
                    377:   if (id <= 0 || id > maxHandles)
                    378:     exit(EFI_INVALID_PARAMETER);
                    379: 
                    380:   return handleList[id - 1];
                    381: }
                    382: 
                    383: VOID* getProtocolByHandleId(UINTN id, EFI_GUID* protocol) {
                    384:   VOID* if0;
                    385:   EFI_HANDLE handle = getHandleById(id);
                    386: 
                    387:   status = BS->HandleProtocol(handle, protocol, &if0);
                    388:   if (EFI_ERROR(status))
                    389:     exit(status);
                    390:   return if0;
                    391: }
                    392: 
                    393: UINTN getHandleId(EFI_HANDLE handle) {
                    394:   UINTN i;
                    395: 
                    396:   initHandleList();
                    397: 
                    398:   for (i = 0; i < maxHandles; ++i) {
                    399:     if (handleList[i] == handle)
                    400:       return i + 1;
                    401:   }
                    402:   exit(EFI_INVALID_PARAMETER);
                    403:   return 0;
                    404: }
                    405: 
                    406: UINT32 getDefaultHexArg(CHAR16* id, UINTN arg, UINT32 dflt) {
                    407:   if (arg >= argc) {
                    408:     // TODO: Get variable named id and return it if present
                    409:     return dflt;
                    410:   }
                    411: 
                    412:   return xtoi(argv[arg]);
                    413: }
                    414: 
                    415: CHAR16* getDeviceName(EFI_HANDLE handle) {
                    416:   EFI_COMPONENT_NAME_PROTOCOL* pComponentName = NULL;
                    417:   CHAR16* pName = NULL;
                    418:   EFI_DEVICE_PATH* path;
                    419: 
                    420:   BS->HandleProtocol(handle, &ComponentNameProtocol, &pComponentName);
                    421:   if (pComponentName)
                    422:     pComponentName->GetControllerName(pComponentName, handle, NULL, "eng", &pName);
                    423:   if (pName)
                    424:     return pName;
                    425: 
                    426:   path = DevicePathFromHandle(handle);
                    427:   return path ? DevicePathToStr(path) : L"Unknown";
                    428: }
                    429: 
                    430: void dumpHandles(VOID* protocol) {
                    431:   UINTN i, handleCount;
                    432:   EFI_HANDLE* handles;
                    433: 
                    434:   LibLocateHandle(ByProtocol, protocol, NULL, &handleCount, &handles);
                    435:   for (i = 0; i < handleCount; ++i) {
                    436:     Print(L"%02x: %s\n", getHandleId(handles[i]), getDeviceName(handles[i]));
                    437:   }
                    438: }
                    439: 
                    440: void dumpHex(VOID* buffer, UINTN bufferOffset, UINTN size, UINTN displayOffset) {
                    441:   UINTN lines = (size + 15) / 16;
                    442:   UINT8* ptr = ((UINT8*)buffer) + bufferOffset;
                    443:   while (lines--) {
                    444:     UINTN i;
                    445: 
                    446:     Print(L"%X: ", displayOffset);
                    447:     for (i = 0; i < 15; ++i) {
                    448:       if (i < size)
                    449:         Print(L"%02x", ptr[i]);
                    450:       else
                    451:         Print(L"  ");
                    452:       Print(L"%c", i == 7 ? '-' : ' ');
                    453:     }
                    454: 
                    455:     Print(L" *");
                    456:     for (i = 0; i < 15 && i < size; ++i) {
                    457:       CHAR16 c = ptr[i];
                    458:       Print(L"%c", c < 0x20 || c > 0x7f ? '.' : c);
                    459:     }
                    460:     Print(L"*\n");
                    461: 
                    462:     displayOffset += 16;
                    463:     ptr += 16;
                    464:     size -= 16;
                    465:   }
                    466: }
                    467: 
                    468: CHAR16 kbdGet() {
                    469:   EFI_INPUT_KEY key;
                    470:   EFI_STATUS status;
                    471: 
                    472:   status = pKeyboard->ReadKeyStroke(pKeyboard, &key);
                    473:   if (status != EFI_SUCCESS)
                    474:     return 0;
                    475:   return key.UnicodeChar ? key.UnicodeChar : key.ScanCode | 0x8000;
                    476: }
                    477: 
                    478: CHAR16 kbdGetKey() {
                    479:   CHAR16 k;
                    480:   while (!(k = kbdGet()));
                    481:   return k;
                    482: }
                    483: 
                    484: void* decompress(void* ptr, UINT32 size) {
                    485:   UINT32 destSize, scratchSize;
                    486:   VOID* destBuffer;
                    487:   VOID* scratchBuffer;
                    488: 
                    489:   // Get buffer sizes
                    490:   status = pDecompress->GetInfo(pDecompress, ptr, size, &destSize, &scratchSize);
                    491:   if (EFI_ERROR(status)) {
                    492:     Print(L"decompress: Error getting decompress info for %X: %r\n", ptr, status);
                    493:     return 0;
                    494:   }
                    495: 
                    496:   // Allocate buffers
                    497:   destBuffer    = AllocatePool(destSize);
                    498:   if (!destBuffer) {
                    499:     Print(L"decompress: Unable to allocate %,d bytes for target decompress buffer\n", destSize);
                    500:     return 0;
                    501:   }
                    502: 
                    503:   scratchBuffer = AllocatePool(scratchSize);
                    504:   if (!scratchBuffer) {
                    505:     Print(L"decompress: Unable to allocate %,d bytes for scratch decompress buffer\n", scratchBuffer);
                    506:     FreePool(destBuffer);
                    507:     return 0;
                    508:   }
                    509: 
                    510:   // Decompress
                    511:   status = pDecompress->Decompress(pDecompress, ptr, size, destBuffer, destSize, scratchBuffer, scratchSize);
                    512:   if (EFI_ERROR(status)) {
                    513:     Print(L"decompress: Error decompressing file at %X: %r\n", ptr, status);
                    514:     FreePool(scratchBuffer);
                    515:     FreePool(destBuffer);
                    516:     return 0;
                    517:   }
                    518: 
                    519:   // Release scratch buffer
                    520:   FreePool(scratchBuffer);
                    521:   return destBuffer;
                    522: }
                    523: 
                    524: void tgaDraw(TGA* tga, UINTN x, UINTN y, TGA_ALIGNMENT alignment) {
                    525:   UINTN horizAlign = alignment & tgaHorizontalMask;
                    526:   UINTN vertAlign = alignment & tgaVerticalMask;
                    527: 
                    528:   // See if this is a valid TGA object
                    529:   if (tga->colorMapType != 0 || tga->imageType != 2 || tga->bits != 32 || tga->identSize != 0) {
                    530:     Print(L"Invalid TGA %X\n", tga);
                    531:     return;
                    532:   }
                    533:   if ((tga->descriptor & 0x20) == 0) {
                    534:     static EFI_UGA_PIXEL buffer[2048];
                    535:     UINTN half = tga->height / 2;
                    536:     UINTN slSize = tga->width * sizeof(EFI_UGA_PIXEL);
                    537:     UINTN y;
                    538: 
                    539:     for (y = 0; y < half; ++y) {
                    540:       void* sl1 = tga->data + y * tga->width;
                    541:       void* sl2 = tga->data + (tga->height - y - 1) * tga->width;
                    542:       CopyMem(buffer, sl1, slSize);
                    543:       CopyMem(sl1   , sl2, slSize);
                    544:       CopyMem(sl2, buffer, slSize);
                    545:     }
                    546:     tga->descriptor |= 0x20;
                    547:   }
                    548:   x = horizAlign == tgaLeft ? x :
                    549:       horizAlign == tgaRight ? x - tga->width : x - tga->width / 2;
                    550:   y = vertAlign  == tgaTop ? y :
                    551:       vertAlign  == tgaBottom ? y - tga->height : y - tga->height / 2;
                    552: 
                    553:   if (pUgaDraw)
                    554:     pUgaDraw->Blt(pUgaDraw, tga->data, EfiUgaBltBufferToVideo, 0, 0, x, y, tga->width, tga->height, 0);
                    555: }
                    556: 
                    557: CHAR16* StrStr(CHAR16* str, CHAR16* str2) {
                    558:   UINTN len, len2, i;
                    559: 
                    560:   len = StrLen(str);
                    561:   len2 = StrLen(str2);
                    562:   for (i = 0; i + len2 <= len; ++i) {
                    563:     if (!StrnCmp(str + i, str2, len2))
                    564:       return str + i;
                    565:   }
                    566:   return 0;
                    567: }
                    568: 
                    569: int StrEndsWith(CHAR16* str, CHAR16* str2) {
                    570:   UINTN len, len2;
                    571: 
                    572:   len = StrLen(str);
                    573:   len2 = StrLen(str2);
                    574:   if (len2 > len)
                    575:     return 0;
                    576: 
                    577:   return !StrCmp(str + len - len2, str2);
                    578: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.