48. UNI 파일 및 HiiLib를 사용하여 HII String 패키지 게시 및 작업하기
NewPackageList 함수를 직접 사용하여 새 문자열 패키지를 추가하는 것은 꽤 어려운 작업이다. 우리는몇 개의 문자열만 추가했으며 패키지 목록에 필요한 데이터 배열을 동적으로 계산하지도 않았다는 점을 명심해야한다. 또한 글꼴/양식/이미지/등등을 추가하려면 이러한 패키지의 형식을 조사하고 필요한 함수도 작성해야한다.
EDKII가 이러한 작업을 단순화하기 위해 무엇을 제공할 수 있는지 확인해보자. 이번 장에서는 특히 문자열 패키지 생성을 단순화하는 방법에 대해 알아볼 것이다.
지금 애플리케이션을 빌드하면 이 파일이 일반 빌드 파일인 Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI/DEBUG/HIIStringsUNIStrDefs.h와 함께 생성된다.
이 파일의 이름은 INF 파일의 BASE_NAME 값으로 구성되며 기본적으로 <BASE_NAME>StrDefs.h이다.
이 파일을 살펴보면,
extern unsigned char HIIStringsUNIStrings[];
이것은 필요한 문자열 패키지 데이터가 있는 배열이다. HiiLib의 HiiAddPackages 함수로 전달하여 HII 패키지를 데이터베이스에 추가하고 새 패키지 목록을 생성한다.
/**
Registers a list of packages in the HII Database and returns the HII Handle
associated with that registration. If an HII Handle has already been registered
with the same PackageListGuid and DeviceHandle, then NULL is returned. If there
are not enough resources to perform the registration, then NULL is returned.
If an empty list of packages is passed in, then NULL is returned. If the size of
the list of package is 0, then NULL is returned.
The variable arguments are pointers that point to package headers defined
by UEFI VFR compiler and StringGather tool.
#pragma pack (push, 1)
typedef struct {
UINT32 BinaryLength;
EFI_HII_PACKAGE_HEADER PackageHeader;
} EDKII_AUTOGEN_PACKAGES_HEADER;
#pragma pack (pop)
@param[in] PackageListGuid The GUID of the package list.
@param[in] DeviceHandle If not NULL, the Device Handle on which
an instance of DEVICE_PATH_PROTOCOL is installed.
This Device Handle uniquely defines the device that
the added packages are associated with.
@param[in] ... The variable argument list that contains pointers
to packages terminated by a NULL.
@retval NULL An HII Handle has already been registered in the HII Database with
the same PackageListGuid and DeviceHandle.
@retval NULL The HII Handle could not be created.
@retval NULL An empty list of packages was passed in.
@retval NULL All packages are empty.
@retval Other The HII Handle associated with the newly registered package list.
**/
EFI_HII_HANDLE
EFIAPI
HiiAddPackages (
IN CONST EFI_GUID *PackageListGuid,
IN EFI_HANDLE DeviceHandle OPTIONAL,
...
)
;
이러한 것들로 패키지 목록 생성은 다음과 같이 간단하게 할 수 있다.
...#include<Library/HiiLib.h>EFI_STATUSEFIAPIUefiMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ){ EFI_HII_HANDLE Handle=HiiAddPackages(&gHIIStringsUNIGuid,NULL, HIIStringsUNIStrings,NULL);if (Handle==NULL) {Print(L"Error! Can't perform HiiAddPackages\n");return EFI_INVALID_PARAMETER; }return EFI_SUCCESS;}
우리의 애플리케이션 INF 파일의 LibraryClasses 섹션에 HiiLib를 포함하는 것을 잊지 말자.
/**
Retrieves a string from a string package in a specific language specified in Language
or in the best lanaguage. See HiiGetStringEx () for the details.
@param[in] HiiHandle A handle that was previously registered in the HII Database.
@param[in] StringId The identifier of the string to retrieved from the string
package associated with HiiHandle.
@param[in] Language The language of the string to retrieve. If this parameter
is NULL, then the current platform language is used. The
format of Language must follow the language format assumed in
the HII Database.
@retval NULL The string specified by StringId is not present in the string package.
@retval Other The string was returned.
**/
EFI_STRING
EFIAPI
HiiGetString (
IN EFI_HII_HANDLE HiiHandle,
IN EFI_STRING_ID StringId,
IN CONST CHAR8 *Language OPTIONAL
);
STRING_TOKEN 매크로를 사용하여 문자열을 참조하는 것이 얼마나 중요한지 알 수 있지만 실제로 이 매크로는 그 값에 아무런 영향을 미치지 않는다는 점을 잘 기억해야 한다. (https://github.com/tianocore/edk2/blob/master/BaseTools/Source /C/Include/Common/UefiInternalFormRepresentation.h)
//// References to string tokens must use this macro to enable scanning for// token usages.////// STRING_TOKEN is not defined in UEFI specification. But it is placed// here for the easy access by C files and VFR source files.//#defineSTRING_TOKEN(t) t
Best Language
HiiGetString 라이브러리 기능은 직접 프로토콜 사용보다 간단할 뿐만 아니라 또 다른 유용한 기능이 존재한다. 바로 대상 언어를 제공하지 않고 HiiGetString을 호출할 수 있다는 점이다. 이 방법을 쓰면 함수는 어떤 언어를 사용하는 것이 더 나은지 스스로 결정한다. 이렇게 하면 PlatformLang 런타임 변수의 값에 따라 최상의 언어가 선택된다. gRT->GetNextVariableName/gRT->GetVariable 을 통해 런타임 변수 작업을 했던 것이 기억날 것이다. PlatformLang도 그 중 하나였다. ListVariables.efi 애플리케이션을 통해 이 옵션이 gEfiGlobalVariableGuid 아래에 있음을 발견했었다.
Print(L"Best language ID=1: %s\n",HiiGetString(Handle,1,NULL));Print(L"Best language ID=2: %s\n",HiiGetString(Handle,STRING_TOKEN(STR_HELLO),NULL));Print(L"Best langiage ID=3: %s\n",HiiGetString(Handle,STRING_TOKEN(STR_BYE),NULL));
애플리케이션을 빌드 후 OVMF에서 실행해보면 다음과 같은 결과를 볼 수 있다.
FS0:\> HIIStringsUNI.efi
en-US ID=1: English
en-US ID=2: Hello!
en-US ID=3: Bye!
fr-FR ID=1: Francais
fr-FR ID=2: Bonjour!
fr-FR ID=3: Au revoir!
Best language ID=1: English
Best language ID=2: Hello!
Best language ID=3: Bye!
이제 UEFI Shell에서 exit을 실행하여 BIOS 설정으로 이동한 후 언어를 프랑스어로 변경한다. QEMU를 닫고 다시 실행해보면 이제 최상의 언어에 대한 출력은 fr-FR String 패키지에서 나오는 것을 볼 수 있다.
FS0:\> HIIStringsUNI.efi
en-US ID=1: English
en-US ID=2: Hello!
en-US ID=3: Bye!
fr-FR ID=1: Francais
fr-FR ID=2: Bonjour!
fr-FR ID=3: Au revoir!
Best language ID=1: Francais
Best language ID=2: Bonjour!
Best language ID=3: Au revoir!
또한 보다 일반적인 언어 이름으로 HiiGetString을 호출할 수 있는 기능이 있다. 예를 들어 fr-FR 대신 fr을 써도 여전히 올바른 문자열 패키지를 선택한다.