2019-09-03 11:58:42 +02:00
|
|
|
/** @file
|
|
|
|
Utility routines used by boot maintenance modules.
|
|
|
|
|
|
|
|
Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR>
|
|
|
|
This program and the accompanying materials
|
|
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
|
|
|
|
**/
|
|
|
|
|
2020-08-17 21:40:52 +02:00
|
|
|
#include <Platform.h> // Only use angled for Platform, else, xcode project won't compile
|
2021-02-06 18:16:46 +01:00
|
|
|
#include <Efi.h>
|
2019-09-03 11:58:42 +02:00
|
|
|
//#include "IO.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
Find the first instance of this Protocol
|
|
|
|
in the system and return it's interface.
|
|
|
|
|
|
|
|
|
|
|
|
@param ProtocolGuid Provides the protocol to search for
|
|
|
|
@param Interface On return, a pointer to the first interface
|
|
|
|
that matches ProtocolGuid
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS A protocol instance matching ProtocolGuid was found
|
|
|
|
@retval EFI_NOT_FOUND No protocol instances were found that match ProtocolGuid
|
|
|
|
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
EfiLibLocateProtocol (
|
|
|
|
IN EFI_GUID *ProtocolGuid,
|
2020-10-03 19:02:31 +02:00
|
|
|
OUT void **Interface
|
2019-09-03 11:58:42 +02:00
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
|
|
|
|
Status = gBS->LocateProtocol (
|
|
|
|
ProtocolGuid,
|
|
|
|
NULL,
|
2020-10-03 19:02:31 +02:00
|
|
|
(void **) Interface
|
2019-09-03 11:58:42 +02:00
|
|
|
);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
Function opens and returns a file handle to the root directory of a volume.
|
|
|
|
|
|
|
|
@param DeviceHandle A handle for a device
|
|
|
|
|
|
|
|
@return A valid file handle or NULL is returned
|
|
|
|
|
|
|
|
**/
|
2020-10-03 19:02:31 +02:00
|
|
|
EFI_FILE*
|
2019-09-03 11:58:42 +02:00
|
|
|
EfiLibOpenRoot (
|
|
|
|
IN EFI_HANDLE DeviceHandle
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume;
|
2020-10-03 19:02:31 +02:00
|
|
|
EFI_FILE* File;
|
2019-09-03 11:58:42 +02:00
|
|
|
|
|
|
|
File = NULL;
|
|
|
|
|
|
|
|
//
|
|
|
|
// File the file system interface to the device
|
|
|
|
//
|
|
|
|
Status = gBS->HandleProtocol (
|
|
|
|
DeviceHandle,
|
|
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
2020-10-03 19:02:31 +02:00
|
|
|
(void **) &Volume
|
2019-09-03 11:58:42 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
//
|
|
|
|
// Open the root directory of the volume
|
|
|
|
//
|
2020-04-23 11:08:10 +02:00
|
|
|
if (!EFI_ERROR(Status)) {
|
2019-09-03 11:58:42 +02:00
|
|
|
Status = Volume->OpenVolume (
|
|
|
|
Volume,
|
|
|
|
&File
|
|
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
|
|
// Done
|
|
|
|
//
|
2020-04-23 11:08:10 +02:00
|
|
|
return EFI_ERROR(Status) ? NULL : File;
|
2019-09-03 11:58:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
2020-08-09 17:55:30 +02:00
|
|
|
Function gets the file system information from an open file descriptor.
|
2019-09-03 11:58:42 +02:00
|
|
|
|
|
|
|
|
|
|
|
@param FHand The file handle.
|
|
|
|
|
|
|
|
@return A pointer to a buffer with file information.
|
|
|
|
@retval NULL is returned if failed to get Volume Label Info.
|
|
|
|
|
|
|
|
**/
|
2020-08-09 17:55:30 +02:00
|
|
|
XStringW
|
2019-09-03 11:58:42 +02:00
|
|
|
EfiLibFileSystemVolumeLabelInfo (
|
2020-10-03 19:02:31 +02:00
|
|
|
const EFI_FILE* FHand
|
2019-09-03 11:58:42 +02:00
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_FILE_SYSTEM_VOLUME_LABEL *VolumeInfo = NULL;
|
|
|
|
UINTN Size = 0;
|
2020-08-09 17:55:30 +02:00
|
|
|
XStringW returnValue;
|
2019-09-03 11:58:42 +02:00
|
|
|
|
2020-10-03 19:02:31 +02:00
|
|
|
Status = FHand->GetInfo(FHand, &gEfiFileSystemVolumeLabelInfoIdGuid, &Size, VolumeInfo);
|
2019-09-03 11:58:42 +02:00
|
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
|
|
// inc size by 2 because some drivers (HFSPlus.efi) do not count 0 at the end of file name
|
|
|
|
Size += 2;
|
2020-08-15 15:47:56 +02:00
|
|
|
VolumeInfo = (__typeof__(VolumeInfo))AllocateZeroPool(Size);
|
2019-09-03 11:58:42 +02:00
|
|
|
Status = FHand->GetInfo (FHand, &gEfiFileSystemVolumeLabelInfoIdGuid, &Size, VolumeInfo);
|
|
|
|
// Check to make sure this isn't actually EFI_FILE_SYSTEM_INFO
|
|
|
|
if (!EFI_ERROR(Status))
|
|
|
|
{
|
|
|
|
EFI_FILE_SYSTEM_INFO *FSInfo = (EFI_FILE_SYSTEM_INFO *)VolumeInfo;
|
2020-08-09 17:55:30 +02:00
|
|
|
returnValue.takeValueFrom(FSInfo->VolumeLabel);
|
2019-09-03 11:58:42 +02:00
|
|
|
}
|
|
|
|
FreePool(VolumeInfo);
|
|
|
|
}
|
2020-08-09 17:55:30 +02:00
|
|
|
return returnValue;
|
2019-09-03 11:58:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Compare strings case insensitive
|
|
|
|
// return 0 if strings are equal not accounting match case
|
|
|
|
INTN
|
|
|
|
StriCmp (
|
|
|
|
IN CONST CHAR16 *FirstS,
|
|
|
|
IN CONST CHAR16 *SecondS
|
|
|
|
)
|
|
|
|
{
|
|
|
|
|
|
|
|
while (*FirstS != L'\0') {
|
|
|
|
if ( (((*FirstS >= 'a') && (*FirstS <= 'z')) ? (*FirstS - ('a' - 'A')) : *FirstS ) !=
|
|
|
|
(((*SecondS >= 'a') && (*SecondS <= 'z')) ? (*SecondS - ('a' - 'A')) : *SecondS ) ) return 1;
|
|
|
|
FirstS++;
|
|
|
|
SecondS++;
|
|
|
|
}
|
|
|
|
return *SecondS; //if all chars equal then compare sizes, last char in SecondS will be 0
|
|
|
|
}
|
|
|
|
|
2021-09-28 10:28:45 +02:00
|
|
|
// If Null-terminated strings are case insensitive equal or its sSize symbols are equal then true
|
|
|
|
XBool
|
2020-04-28 12:49:24 +02:00
|
|
|
AsciiStriNCmp(
|
2019-09-03 11:58:42 +02:00
|
|
|
IN CONST CHAR8 *FirstS,
|
|
|
|
IN CONST CHAR8 *SecondS,
|
|
|
|
IN CONST UINTN sSize
|
|
|
|
)
|
|
|
|
{
|
|
|
|
INTN i = sSize;
|
|
|
|
while ( i && (*FirstS != '\0') ) {
|
|
|
|
if ( (((*FirstS >= 'a') && (*FirstS <= 'z')) ? (*FirstS - ('a' - 'A')) : *FirstS ) !=
|
2021-09-28 10:28:45 +02:00
|
|
|
(((*SecondS >= 'a') && (*SecondS <= 'z')) ? (*SecondS - ('a' - 'A')) : *SecondS ) ) return false;
|
2019-09-03 11:58:42 +02:00
|
|
|
FirstS++;
|
|
|
|
SecondS++;
|
|
|
|
i--;
|
|
|
|
}
|
2021-09-28 10:28:45 +02:00
|
|
|
return true;
|
2019-09-03 11:58:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Case insensitive search of WhatString in WhereString
|
2021-09-28 10:28:45 +02:00
|
|
|
XBool
|
2019-09-03 11:58:42 +02:00
|
|
|
AsciiStrStriN (
|
|
|
|
IN CONST CHAR8 *WhatString,
|
|
|
|
IN CONST UINTN sWhatSize,
|
|
|
|
IN CONST CHAR8 *WhereString,
|
|
|
|
IN CONST UINTN sWhereSize
|
|
|
|
)
|
|
|
|
{
|
|
|
|
INTN i = sWhereSize;
|
2021-09-28 10:28:45 +02:00
|
|
|
XBool Found = false;
|
|
|
|
if (sWhatSize > sWhereSize) return false;
|
2019-09-03 11:58:42 +02:00
|
|
|
for (; i && !Found; i--) {
|
|
|
|
Found = AsciiStriNCmp(WhatString, WhereString, sWhatSize);
|
|
|
|
WhereString++;
|
|
|
|
}
|
|
|
|
return Found;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
Function gets the file information from an open file descriptor, and stores it
|
|
|
|
in a buffer allocated from pool.
|
|
|
|
|
|
|
|
@param FHand File Handle.
|
|
|
|
|
|
|
|
@return A pointer to a buffer with file information or NULL is returned
|
|
|
|
|
|
|
|
**/
|
|
|
|
EFI_FILE_INFO *
|
|
|
|
EfiLibFileInfo (
|
2020-10-03 19:02:31 +02:00
|
|
|
const EFI_FILE* FHand
|
2019-09-03 11:58:42 +02:00
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_FILE_INFO *FileInfo = NULL;
|
|
|
|
UINTN Size = 0;
|
|
|
|
|
|
|
|
Status = FHand->GetInfo (FHand, &gEfiFileInfoGuid, &Size, FileInfo);
|
|
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
|
|
// inc size by 2 because some drivers (HFSPlus.efi) do not count 0 at the end of file name
|
|
|
|
Size += 2;
|
2020-08-15 15:47:56 +02:00
|
|
|
FileInfo = (__typeof__(FileInfo))AllocateZeroPool(Size);
|
2019-09-03 11:58:42 +02:00
|
|
|
Status = FHand->GetInfo (FHand, &gEfiFileInfoGuid, &Size, FileInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
return EFI_ERROR(Status)?NULL:FileInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
EFI_FILE_SYSTEM_INFO *
|
|
|
|
EfiLibFileSystemInfo (
|
2020-10-03 19:02:31 +02:00
|
|
|
const EFI_FILE* FHand
|
2019-09-03 11:58:42 +02:00
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_FILE_SYSTEM_INFO *FileSystemInfo = NULL;
|
|
|
|
UINTN Size = 0;
|
|
|
|
|
|
|
|
Status = FHand->GetInfo (FHand, &gEfiFileSystemInfoGuid, &Size, FileSystemInfo);
|
|
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
|
|
// inc size by 2 because some drivers (HFSPlus.efi) do not count 0 at the end of file name
|
|
|
|
Size += 2;
|
2020-08-15 15:47:56 +02:00
|
|
|
FileSystemInfo = (__typeof__(FileSystemInfo))AllocateZeroPool(Size);
|
2019-09-03 11:58:42 +02:00
|
|
|
Status = FHand->GetInfo (FHand, &gEfiFileSystemInfoGuid, &Size, FileSystemInfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
return EFI_ERROR(Status)?NULL:FileSystemInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Function is used to determine the number of device path instances
|
|
|
|
that exist in a device path.
|
|
|
|
|
|
|
|
|
|
|
|
@param DevicePath A pointer to a device path data structure.
|
|
|
|
|
|
|
|
@return This function counts and returns the number of device path instances
|
|
|
|
in DevicePath.
|
|
|
|
|
|
|
|
**/
|
|
|
|
UINTN
|
|
|
|
EfiDevicePathInstanceCount (
|
|
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UINTN Count;
|
|
|
|
UINTN Size;
|
|
|
|
|
|
|
|
Count = 0;
|
|
|
|
while (GetNextDevicePathInstance (&DevicePath, &Size) != NULL) {
|
|
|
|
Count += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Count;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Adjusts the size of a previously allocated buffer.
|
|
|
|
|
|
|
|
|
|
|
|
@param OldPool - A pointer to the buffer whose size is being adjusted.
|
|
|
|
@param OldSize - The size of the current buffer.
|
|
|
|
@param NewSize - The size of the new buffer.
|
|
|
|
|
|
|
|
@return The newly allocated buffer.
|
|
|
|
@retval NULL Allocation failed.
|
|
|
|
|
|
|
|
**/
|
2020-10-03 19:02:31 +02:00
|
|
|
void *
|
2019-09-03 11:58:42 +02:00
|
|
|
EfiReallocatePool (
|
2020-10-03 19:02:31 +02:00
|
|
|
IN void *OldPool,
|
2019-09-03 11:58:42 +02:00
|
|
|
IN UINTN OldSize,
|
|
|
|
IN UINTN NewSize
|
|
|
|
)
|
|
|
|
{
|
2020-10-03 19:02:31 +02:00
|
|
|
void *NewPool;
|
2019-09-03 11:58:42 +02:00
|
|
|
|
|
|
|
NewPool = NULL;
|
|
|
|
if (NewSize != 0) {
|
2020-08-15 15:47:56 +02:00
|
|
|
NewPool = (__typeof__(NewPool))AllocateZeroPool(NewSize);
|
2019-09-03 11:58:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (OldPool != NULL) {
|
|
|
|
if (NewPool != NULL) {
|
2020-05-01 18:26:28 +02:00
|
|
|
CopyMem(NewPool, OldPool, OldSize < NewSize ? OldSize : NewSize);
|
2019-09-03 11:58:42 +02:00
|
|
|
}
|
|
|
|
|
2020-04-23 11:08:10 +02:00
|
|
|
FreePool(OldPool);
|
2019-09-03 11:58:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return NewPool;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Compare two EFI_TIME data.
|
|
|
|
|
|
|
|
|
|
|
|
@param FirstTime - A pointer to the first EFI_TIME data.
|
|
|
|
@param SecondTime - A pointer to the second EFI_TIME data.
|
|
|
|
|
2021-09-28 10:28:45 +02:00
|
|
|
@retval true The FirstTime is not later than the SecondTime.
|
|
|
|
@retval false The FirstTime is later than the SecondTime.
|
2019-09-03 11:58:42 +02:00
|
|
|
|
|
|
|
**/
|
2021-09-28 10:28:45 +02:00
|
|
|
XBool
|
2019-09-03 11:58:42 +02:00
|
|
|
TimeCompare (
|
|
|
|
IN EFI_TIME *FirstTime,
|
|
|
|
IN EFI_TIME *SecondTime
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (FirstTime->Year != SecondTime->Year) {
|
2021-09-28 10:28:45 +02:00
|
|
|
return (XBool) (FirstTime->Year < SecondTime->Year);
|
2019-09-03 11:58:42 +02:00
|
|
|
} else if (FirstTime->Month != SecondTime->Month) {
|
2021-09-28 10:28:45 +02:00
|
|
|
return (XBool) (FirstTime->Month < SecondTime->Month);
|
2019-09-03 11:58:42 +02:00
|
|
|
} else if (FirstTime->Day != SecondTime->Day) {
|
2021-09-28 10:28:45 +02:00
|
|
|
return (XBool) (FirstTime->Day < SecondTime->Day);
|
2019-09-03 11:58:42 +02:00
|
|
|
} else if (FirstTime->Hour != SecondTime->Hour) {
|
2021-09-28 10:28:45 +02:00
|
|
|
return (XBool) (FirstTime->Hour < SecondTime->Hour);
|
2019-09-03 11:58:42 +02:00
|
|
|
} else if (FirstTime->Minute != SecondTime->Minute) {
|
2021-09-28 10:28:45 +02:00
|
|
|
return (XBool) (FirstTime->Minute < SecondTime->Minute);
|
2019-09-03 11:58:42 +02:00
|
|
|
} else if (FirstTime->Second != SecondTime->Second) {
|
2021-09-28 10:28:45 +02:00
|
|
|
return (XBool) (FirstTime->Second < SecondTime->Second);
|
2019-09-03 11:58:42 +02:00
|
|
|
}
|
|
|
|
|
2021-09-28 10:28:45 +02:00
|
|
|
return (XBool) (FirstTime->Nanosecond <= SecondTime->Nanosecond);
|
2019-09-03 11:58:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
Translate VT-UTF8 characters into one Unicode character.
|
|
|
|
|
|
|
|
UTF8 Encoding Table
|
|
|
|
Bits per Character | Unicode Character Range | Unicode Binary Encoding | UTF8 Binary Encoding
|
|
|
|
0-7 | 0x0000 - 0x007F | 00000000 0xxxxxxx | 0xxxxxxx
|
|
|
|
8-11 | 0x0080 - 0x07FF | 00000xxx xxxxxxxx | 110xxxxx 10xxxxxx
|
|
|
|
12-16 | 0x0800 - 0xFFFF | xxxxxxxx xxxxxxxx | 1110xxxx 10xxxxxx 10xxxxxx
|
|
|
|
|
|
|
|
$ U+0024 10 0100 00100100 24
|
|
|
|
¢ U+00A2 1010 0010 11000010 10100010 C2 A2
|
|
|
|
€ U+20AC 0010 0000 1010 1100 11100010 10000010 10101100 E2 82 AC
|
|
|
|
𐍈 U+10348 1 0000 0011 0100 1000 11110000 10010000 10001101 10001000 F0 90 8D 88
|
|
|
|
*/
|
|
|
|
|
|
|
|
CHAR8* GetUnicodeChar(CHAR8 *s, CHAR16* UnicodeChar)
|
|
|
|
{
|
|
|
|
INT8 ValidBytes = 0;
|
|
|
|
UINT8 Byte0, Byte1, Byte2;
|
|
|
|
UINT8 UnicodeByte0, UnicodeByte1;
|
|
|
|
CHAR16 A = L'\0';
|
|
|
|
if (*s == '&') {
|
|
|
|
if (AsciiStrStr(s, "&#x") !=0 ) {
|
|
|
|
s += 3;
|
|
|
|
while (IS_HEX(*s) || IS_DIGIT(*s)) {
|
|
|
|
A <<= 4;
|
|
|
|
if (IS_DIGIT(*s)) {
|
|
|
|
A += *s - 0x30;
|
|
|
|
} else if (IS_UPPER(*s)) {
|
|
|
|
A += *s - 0x41 + 10;
|
|
|
|
} else {
|
|
|
|
A += *s - 0x61 + 10;
|
|
|
|
}
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
} else if (AsciiStrStr(s, "&") !=0 ) {
|
|
|
|
A = 0x26; //&
|
|
|
|
s += 5;
|
|
|
|
} else if (AsciiStrStr(s, """) !=0 ) {
|
|
|
|
A = 0x22; //"
|
|
|
|
s += 6;
|
|
|
|
} else if (AsciiStrStr(s, "<") !=0 ) {
|
|
|
|
A = 0x3C; //<
|
|
|
|
s += 4;
|
|
|
|
} else if (AsciiStrStr(s, ">") !=0 ) {
|
|
|
|
A = 0x3E; //>
|
|
|
|
s += 4;
|
|
|
|
} else if (AsciiStrStr(s, " ") !=0 ) {
|
|
|
|
A = 0xA0; //>
|
|
|
|
s += 6;
|
|
|
|
}
|
|
|
|
*UnicodeChar = A;
|
|
|
|
} else {
|
|
|
|
if ((*s & 0x80) == 0) {
|
|
|
|
ValidBytes = 1;
|
|
|
|
} else if ((*s & 0xe0) == 0xc0) {
|
|
|
|
ValidBytes = 2;
|
|
|
|
} else if ((*s & 0xf0) == 0xe0) {
|
|
|
|
ValidBytes = 3;
|
|
|
|
}
|
|
|
|
switch (ValidBytes) {
|
|
|
|
case 1:
|
|
|
|
//
|
|
|
|
// one-byte utf8 code
|
|
|
|
//
|
|
|
|
*UnicodeChar = (UINT16) (*s++);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
//
|
|
|
|
// two-byte utf8 code
|
|
|
|
//
|
|
|
|
Byte1 = *s++; //c2
|
|
|
|
Byte0 = *s++; //a2
|
|
|
|
|
|
|
|
UnicodeByte0 = (UINT8) ((Byte1 << 6) | (Byte0 & 0x3f));
|
|
|
|
UnicodeByte1 = (UINT8) ((Byte1 >> 2) & 0x07);
|
|
|
|
*UnicodeChar = (UINT16) (UnicodeByte0 | (UnicodeByte1 << 8));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
//
|
|
|
|
// three-byte utf8 code
|
|
|
|
// sample E3 90 A1 = 0x3421
|
|
|
|
//
|
|
|
|
Byte2 = *s++;
|
|
|
|
Byte1 = *s++;
|
|
|
|
Byte0 = *s++;
|
|
|
|
|
|
|
|
UnicodeByte0 = (UINT8) ((Byte1 << 6) | (Byte0 & 0x3f));
|
|
|
|
UnicodeByte1 = (UINT8) ((Byte2 << 4) | ((Byte1 >> 2) & 0x0f));
|
|
|
|
*UnicodeChar = (UINT16) (UnicodeByte0 | (UnicodeByte1 << 8));
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|