1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
# String package
String package is a set of strings in one language. Each of the strings in package can be referred by ID. IDs to these strings are not written anywhere but it is supposed that numbering scheme simply starts from ID=1 in every string package and increases by 1 on every string [there are some exceptions, but it is not important now].
The general idea is that when you want to provide translations for your piece of interface you create String packages for each language you want to support. In every of these language string packages the same words should have the same ID. For example:
```
String package (ENG) String package (FR)
___________________ ___________________
ID=1 ... ...
... ...
ID=5 Hello! Bonjour!
... ...
```
In the code strings from the Package list are received from the `(ID, language)` combination. This helps to create UIs that could switch translations easily.
# String package content
String package strarts with a header `EFI_HII_STRING_PACKAGE_HDR` and then contains so called `String blocks`, which can be of different types. And as with the package list, the last element (=String block) has special `END` type.
![String_package](String_package.png?raw=true "String package")
The most important and most common type of String block is UCS2 String block. It contains header which points String block type and a CHAR16 string as a block data. Basically String package is a set of UCS2 String blocks each of which contains one string.
![UCS2_String_block](UCS2_String_block.png?raw=true "UCS2 String block")
# Create String package
Let's look at the header of the String package:
```
Prototype:
typedef struct _EFI_HII_STRING_PACKAGE_HDR {
EFI_HII_PACKAGE_HEADER Header;
UINT32 HdrSize;
UINT32 StringInfoOffset;
CHAR16 LanguageWindow[16];
EFI_STRING_ID LanguageName;
CHAR8 Language[ … ];
} EFI_HII_STRING_PACKAGE_HDR;
Members:
Header The standard package header, where Header.Type = EFI_HII_PACKAGE_STRINGS.
HdrSize Size of this header.
StringInfoOffset Offset, relative to the start of this header, of the string information.
LanguageWindow Specifies the default values placed in the static and dynamic windows
before processing each SCSU-encoded string.
LanguageName String identifier within the current string package of the full name of the
language specified by Language.
Language The null-terminated ASCII string that specifies the language of the strings in the package.
```
`CHAR8 Language` is a string like `en-EN`, `en-US`, `en`, `ru-RU`, ...
As the `Language` size not fixed, the `EFI_HII_STRING_PACKAGE_HDR` has a `HdrSize` field to indicate overall package header size.
Besides this short form, every string package contains a string with a full language name (e.g. `English`), and `LanguageName` field is an ID of this string within a package. Usually it is a first string with an ID=1.
As we've already know the String package would be filled with String blocks. They can be of many types, but for now we'll investigate the most important ones - the `UCS2 String block` and the `End block`.
MdePkg/Include/Uefi/UefiInternalFormRepresentation.h
```
typedef struct {
UINT8 BlockType;
} EFI_HII_STRING_BLOCK;
//
// Value of different string information block types
//
#define EFI_HII_SIBT_END 0x00
...
#define EFI_HII_SIBT_STRING_UCS2 0x14
...
typedef struct _EFI_HII_SIBT_STRING_UCS2_BLOCK {
EFI_HII_STRING_BLOCK Header;
CHAR16 StringText[1]; // <--- String size is not fixed, but to point a fact that this type of block has
} EFI_HII_SIBT_STRING_UCS2_BLOCK; // a string in itself, the header contains one element array
...
typedef struct _EFI_HII_SIBT_END_BLOCK {
EFI_HII_STRING_BLOCK Header;
} EFI_HII_SIBT_END_BLOCK;
```
Now it is time to write a function that can fill package data array from strings:
```
UINT32 InitStringPackage(CHAR8* Ptr, CHAR8* Language, CHAR16** Strings, UINTN StringsLen):
// CHAR8* Ptr - start of the array where we want our package data
// CHAR8* Language - short language form for the `EFI_HII_STRING_PACKAGE_HDR.Language` field
// CHAR16** Strings - array of CHAR16* Strings that would be written to the package
// UINTN StringsLen - how many string there are in the Strings array
//
// Return UINT32 - function return the result package size
```
Here is a function implementation:
```
UINT32 InitStringPackage(CHAR8* Ptr, CHAR8* Language, CHAR16** Strings, UINTN StringsLen)
{
UINT32 Size = 0;
EFI_HII_STRING_PACKAGE_HDR* HIIStringPackageHdr = (EFI_HII_STRING_PACKAGE_HDR*)&Ptr[0];
HIIStringPackageHdr->Header.Type = EFI_HII_PACKAGE_STRINGS;
UINT32 HeaderSize = (UINT32) (AsciiStrSize(Language) - 1 + sizeof (EFI_HII_STRING_PACKAGE_HDR));
HIIStringPackageHdr->HdrSize = HeaderSize;
HIIStringPackageHdr->StringInfoOffset = HeaderSize;
HIIStringPackageHdr->LanguageName = 1; // <--- the String[0] should be a full language name string!
AsciiStrCpyS (HIIStringPackageHdr->Language,
(HeaderSize - OFFSET_OF(EFI_HII_STRING_PACKAGE_HDR,Language)) / sizeof (CHAR8),
Language);
Size += HeaderSize;
for (UINTN i=0; i<StringsLen; i++)
{
EFI_HII_SIBT_STRING_UCS2_BLOCK* StrBlock = (EFI_HII_SIBT_STRING_UCS2_BLOCK*)&Ptr[Size];
StrBlock->Header.BlockType = EFI_HII_SIBT_STRING_UCS2;
StrCpyS(StrBlock->StringText, StrLen(Strings[i])+1, Strings[i]);
Size += sizeof(EFI_HII_SIBT_STRING_UCS2_BLOCK) + StrLen(Strings[i])*2;
}
EFI_HII_SIBT_END_BLOCK* EndBlock = (EFI_HII_SIBT_END_BLOCK*)&Ptr[Size];
EndBlock->Header.BlockType = EFI_HII_SIBT_END;
Size += sizeof(EFI_HII_SIBT_END_BLOCK);
HIIStringPackageHdr->Header.Length = Size;
return Size;
}
```
This function can be called like this:
```
CHAR16* EnStrings[] = {
L"English",
L"Hello",
};
CHAR8* Data = (CHAR8*) AllocateZeroPool(200);
UINT32 PackageSize = InitStringPackage(Data, "en-US", EnStrings, sizeof(EnStrings)/sizeof(EnStrings[0]));
```
Here are we use size `200` for our Data array as a size much bigger that we would need for the package. Ideally we need to perform calculations for the array size, but this lesson is hard enough as it is, so we keep it like that for now.
Now when we have our `InitStringPackage` function that creates string packages we can fill package data in our code.
Here is a complete Package list data generation code:
```
CHAR8* Data = (CHAR8*) AllocateZeroPool(200); // CHEAT! NEEDS CORRECTION FOR YOUR OWN PACKAGES!
UINT32 offset = 0;
EFI_HII_PACKAGE_LIST_HEADER* PackageListHdr = (EFI_HII_PACKAGE_LIST_HEADER*)&Data[offset];
PackageListHdr->PackageListGuid = gHIIStringsCGuid;
offset += sizeof(EFI_HII_PACKAGE_LIST_HEADER);
CHAR16* EnStrings[] = {
L"English",
L"Hello",
};
offset += InitStringPackage(&Data[offset], "en-US", EnStrings, sizeof(EnStrings)/sizeof(EnStrings[0]));
CHAR16* FrStrings[] = {
L"French",
L"Bonjour",
};
offset += InitStringPackage(&Data[offset], "fr-FR", FrStrings, sizeof(FrStrings)/sizeof(FrStrings[0]));
EFI_HII_PACKAGE_HEADER* HIIEndPackageHdr = (EFI_HII_PACKAGE_HEADER*)&Data[offset];
HIIEndPackageHdr->Type = EFI_HII_PACKAGE_END;
HIIEndPackageHdr->Length = sizeof(EFI_HII_PACKAGE_HEADER);
offset += sizeof(EFI_HII_PACKAGE_HEADER);
PackageListHdr->PackageLength = offset;
<...> // Add new package to the HII Database
FreePool(Data);
```
|