3 Copyright (c) 2004, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 String database implementation
25 #include <ctype.h> // for tolower()
27 #include <Common/UefiBaseTypes.h>
28 #include <Common/MultiPhase.h>
29 #include <Common/InternalFormRepresentation.h>
30 #include <Protocol/UgaDraw.h> // for EFI_UGA_PIXEL definition
31 #include <Protocol/Hii.h>
33 #include "EfiUtilityMsgs.h"
34 #include "StrGather.h"
38 #define STRING_OFFSET RELOFST
40 #define STRING_DB_KEY (('S' << 24) | ('D' << 16) | ('B' << 8) | 'K')
42 // Version supported by this tool
44 #define STRING_DB_VERSION 0x00010000
46 #define STRING_DB_MAJOR_VERSION_MASK 0xFFFF0000
47 #define STRING_DB_MINOR_VERSION_MASK 0x0000FFFF
49 #define DEFINE_STR L"// #define"
51 #define LANGUAGE_CODE_WIDTH 4
53 // This is the header that gets written to the top of the
54 // output binary database file.
60 UINT32 NumStringIdenfiers
;
61 UINT32 StringIdentifiersSize
;
66 // When we write out data to the database, we have a UINT16 identifier, which
67 // indicates what follows, followed by the data. Here's the structure.
72 } DB_DATA_ITEM_HEADER
;
74 #define DB_DATA_TYPE_INVALID 0x0000
75 #define DB_DATA_TYPE_STRING_IDENTIFIER 0x0001
76 #define DB_DATA_TYPE_LANGUAGE_DEFINITION 0x0002
77 #define DB_DATA_TYPE_STRING_DEFINITION 0x0003
78 #define DB_DATA_TYPE_LAST DB_DATA_TYPE_STRING_DEFINITION
81 // We have to keep track of a list of languages, each of which has its own
82 // list of strings. Define a structure to keep track of all languages and
83 // their list of strings.
85 typedef struct _STRING_LIST
{
86 struct _STRING_LIST
*Next
;
87 UINT32 Size
; // number of bytes in string, including null terminator
89 WCHAR
*StringName
; // for example STR_ID_TEXT1
91 WCHAR
*Str
; // the actual string
92 UINT16 Flags
; // properties of this string (used, undefined)
95 typedef struct _LANGUAGE_LIST
{
96 struct _LANGUAGE_LIST
*Next
;
97 WCHAR LanguageName
[4];
98 WCHAR
*PrintableLanguageName
;
100 STRING_LIST
*LastString
;
104 // We also keep track of all the string identifier names, which we assign unique
105 // values to. Create a structure to keep track of them all.
107 typedef struct _STRING_IDENTIFIER
{
108 struct _STRING_IDENTIFIER
*Next
;
109 UINT32 Index
; // only need 16 bits, but makes it easier with UINT32
111 UINT16 Flags
; // if someone referenced it via STRING_TOKEN()
114 // Keep our globals in this structure to be as modular as possible.
118 LANGUAGE_LIST
*LanguageList
;
119 LANGUAGE_LIST
*LastLanguageList
;
120 LANGUAGE_LIST
*CurrentLanguage
; // keep track of the last language they used
121 STRING_IDENTIFIER
*StringIdentifier
;
122 STRING_IDENTIFIER
*LastStringIdentifier
;
123 UINT8
*StringDBFileName
;
124 UINT32 NumStringIdentifiers
;
125 UINT32 NumStringIdentifiersReferenced
;
126 STRING_IDENTIFIER
*CurrentStringIdentifier
; // keep track of the last string identifier they added
130 static STRING_DB_DATA mDBData
;
132 static const char *mSourceFileHeader
[] = {
134 "// DO NOT EDIT -- auto-generated file",
136 "// This file is generated by the string gather utility",
147 WCHAR_STRING_LIST
*LanguagesOfInterest
,
148 WCHAR_MATCHING_STRING_LIST
*IndirectionList
153 StringDBFindStringIdentifierByName (
159 StringDBFindStringIdentifierByIndex (
165 StringDBFindLanguageList (
171 StringDBWriteStandardFileHeader (
189 StringDBWriteStringIdentifier (
193 WCHAR
*IdentifierName
198 StringDBReadStringIdentifier (
204 StringDBWriteLanguageDefinition (
207 WCHAR
*PrintableLanguageName
212 StringDBReadLanguageDefinition (
218 StringDBWriteString (
235 StringDBReadGenericString (
243 StringDBWriteGenericString (
250 StringDBAssignStringIndexes (
254 /*****************************************************************************/
259 Constructor function for the string database handler.
269 StringDBConstructor (
273 memset ((char *) &mDBData
, 0, sizeof (STRING_DB_DATA
));
274 mDBData
.CurrentScope
= DuplicateString (L
"NULL");
277 /*****************************************************************************/
282 Destructor function for the string database handler.
296 LANGUAGE_LIST
*NextLang
;
297 STRING_LIST
*NextStr
;
298 STRING_IDENTIFIER
*NextIdentifier
;
300 // Close the database file if it's open
302 if (mDBData
.StringDBFptr
!= NULL
) {
303 fclose (mDBData
.StringDBFptr
);
304 mDBData
.StringDBFptr
= NULL
;
307 // If we've allocated any strings/languages, free them up
309 while (mDBData
.LanguageList
!= NULL
) {
310 NextLang
= mDBData
.LanguageList
->Next
;
312 // Free up all strings for this language
314 while (mDBData
.LanguageList
->String
!= NULL
) {
315 NextStr
= mDBData
.LanguageList
->String
->Next
;
316 FREE (mDBData
.LanguageList
->String
->Str
);
317 FREE (mDBData
.LanguageList
->String
);
318 mDBData
.LanguageList
->String
= NextStr
;
321 FREE (mDBData
.LanguageList
->PrintableLanguageName
);
322 FREE (mDBData
.LanguageList
);
323 mDBData
.LanguageList
= NextLang
;
326 // Free up string identifiers
328 while (mDBData
.StringIdentifier
!= NULL
) {
329 NextIdentifier
= mDBData
.StringIdentifier
->Next
;
330 FREE (mDBData
.StringIdentifier
->StringName
);
331 FREE (mDBData
.StringIdentifier
);
332 mDBData
.StringIdentifier
= NextIdentifier
;
337 if (mDBData
.StringDBFileName
!= NULL
) {
338 FREE (mDBData
.StringDBFileName
);
339 mDBData
.StringDBFileName
= NULL
;
342 // We save a copy of the scope, so free it up if we
345 if (mDBData
.CurrentScope
!= NULL
) {
346 FREE (mDBData
.CurrentScope
);
347 mDBData
.CurrentScope
= NULL
;
351 /*****************************************************************************/
357 Dump the contents of a database to an output C file.
361 FileName - name of the output file to write
362 BaseName - used for the name of the C array defined
363 Languages - list of languages of interest
371 Languages is a pointer to a linked list of languages specified on
372 the command line. Format is "eng" and "spa+cat". For this, print
373 the strings for eng. Print the strings for spa too, but if one is
374 missing look for a cat string and print if it it exists.
378 StringDBDumpCStrings (
381 WCHAR_STRING_LIST
*LanguagesOfInterest
,
382 WCHAR_MATCHING_STRING_LIST
*IndirectionList
387 STRING_LIST
*CurrString
;
388 STRING_LIST EmptyString
;
392 UINT32 BytesThisLine
;
393 EFI_HII_STRING_PACK StringPack
;
397 WCHAR_STRING_LIST
*LOIPtr
;
399 WCHAR
*TempStringPtr
;
401 STRING_IDENTIFIER
*StringIdentifier
;
404 if ((Fptr
= fopen (FileName
, "w")) == NULL
) {
405 Error (NULL
, 0, 0, FileName
, "failed to open output C string file");
409 // Assign index values to the string identifiers
411 StringDBAssignStringIndexes ();
413 // Write the standard header to the output file, then the structure
414 // definition header.
416 StringDBWriteStandardFileHeader (Fptr
);
417 fprintf (Fptr
, "\nunsigned char %s[] = {\n", BaseName
);
419 // If a given string is not defined, then we'll use this one.
421 memset (&EmptyString
, 0, sizeof (EmptyString
));
422 EmptyString
.Size
= sizeof (ZeroString
);
423 EmptyString
.Str
= ZeroString
;
425 // Process each language, then each string for each langage
428 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
430 // If we have a language list, then make sure this language is in that
434 LangName
= Lang
->LanguageName
;
435 if (LanguagesOfInterest
!= NULL
) {
437 for (LOIPtr
= LanguagesOfInterest
; LOIPtr
!= NULL
; LOIPtr
= LOIPtr
->Next
) {
438 if (StrnCmp (LOIPtr
->Str
, Lang
->LanguageName
, LANGUAGE_IDENTIFIER_NAME_LEN
) == 0) {
439 LangName
= LOIPtr
->Str
;
450 // Process each string for this language. We have to make 3 passes on the strings:
451 // Pass1: computes sizes and fill in the string pack header
452 // Pass2: write the array of offsets
453 // Pass3: write the strings
456 // PASS 1: Fill in and print the HII string pack header
458 // Compute the size for this language package and write
459 // the header out. Each string package contains:
461 // Offset[] -- an array of offsets to strings, of type RELOFST each
462 // String[] -- the actual strings themselves
464 AsciiSPrint ( Line
, sizeof(Line
),
465 "\n//******************************************************************************"
466 "\n// Start of string definitions for %s/%s",
468 Lang
->PrintableLanguageName
470 fprintf (Fptr
, "%s", Line
);
471 memset ((char *) &StringPack
, 0, sizeof (EFI_HII_STRING_PACK
));
472 StringPack
.Header
.Type
= EFI_HII_STRING
;
473 StringPack
.NumStringPointers
= (UINT16
) mDBData
.NumStringIdentifiersReferenced
;
475 // First string is the language name. If we're printing all languages, then
476 // it's just the "spa". If we were given a list of languages to print, then it's
477 // the "spacat" string. Compute its offset and fill in
478 // the info in the header. Since we know the language name string's length,
479 // and the printable language name follows it, use that info to fill in the
480 // entry for the printable language name as well.
482 StringPack
.LanguageNameString
= (STRING_OFFSET
) (sizeof (EFI_HII_STRING_PACK
) + (mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
)));
483 StringPack
.PrintableLanguageName
= (STRING_OFFSET
) (StringPack
.LanguageNameString
+ (StrLen (LangName
) + 1) * sizeof (WCHAR
));
485 // Add up the size of all strings so we can fill in our header.
488 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
490 // For the first string (language name), we print out the "spacat" if they
491 // requested it. We set LangName to point to the proper language name string above.
493 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
494 Len
+= (StrLen (LangName
) + 1) * sizeof (WCHAR
);
497 // Find a string with this language.stringname
499 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
500 if (StringIdentifier
== NULL
) {
501 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
505 // Find a matching string if this string identifier was referenced
507 EmptyString
.Flags
= STRING_FLAGS_UNDEFINED
;
509 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
510 CurrString
= StringDBFindString (
512 StringIdentifier
->StringName
,
517 if (NULL
== CurrString
) {
519 // If string for Lang->LanguageName is not found, try to get an English version
521 CurrString
= StringDBFindString (
523 StringIdentifier
->StringName
,
531 if (CurrString
== NULL
) {
532 CurrString
= &EmptyString
;
533 EmptyString
.Flags
|= StringIdentifier
->Flags
;
536 Len
+= CurrString
->Size
;
539 StringPack
.Header
.Length
= sizeof (EFI_HII_STRING_PACK
)
540 + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
)
543 // Write out the header one byte at a time
545 Ptr
= (UINT8
*) &StringPack
;
546 for (TempIndex
= 0; TempIndex
< sizeof (EFI_HII_STRING_PACK
); TempIndex
++, Ptr
++) {
547 if ((TempIndex
& 0x07) == 0) {
548 fprintf (Fptr
, "\n ");
551 fprintf (Fptr
, "0x%02X, ", (UINT32
) *Ptr
);
554 fprintf (Fptr
, "\n // offset 0x%X\n", sizeof (StringPack
));
556 // PASS2 : write the offsets
558 // Traverse the list of strings again and write the array of offsets. The
559 // offset to the first string is the size of the string pack header
560 // plus the size of the offsets array. The other strings follow it.
563 Offset
= sizeof (StringPack
) + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
);
564 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
566 // Write the offset, followed by a useful comment
569 Ptr
= (UINT8
*) &Offset
;
570 for (TempIndex
= 0; TempIndex
< sizeof (STRING_OFFSET
); TempIndex
++) {
571 fprintf (Fptr
, "0x%02X, ", (UINT32
) Ptr
[TempIndex
]);
574 // Find the string name
576 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
577 if (StringIdentifier
== NULL
) {
578 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
582 AsciiSPrint (Line
, sizeof(Line
) , " // offset to string %s (0x%04X)", StringIdentifier
->StringName
, StringIndex
);
583 fprintf (Fptr
, "%s", Line
);
585 // For the first string (language name), we print out the "spacat" if they
586 // requested it. We set LangName to point to the proper language name string above.
588 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
589 Offset
+= (StrLen (LangName
) + 1) * sizeof (WCHAR
);
590 CurrString
= StringDBFindString (
592 StringIdentifier
->StringName
,
599 // Find a matching string
601 CurrString
= StringDBFindString (
603 StringIdentifier
->StringName
,
609 if (NULL
== CurrString
) {
610 CurrString
= StringDBFindString (
612 StringIdentifier
->StringName
,
619 EmptyString
.LanguageName
= Lang
->LanguageName
;
620 if (CurrString
== NULL
) {
621 CurrString
= &EmptyString
;
622 EmptyString
.Flags
= STRING_FLAGS_UNDEFINED
;
623 } else if ((StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) == 0) {
624 CurrString
= &EmptyString
;
625 EmptyString
.Flags
= 0;
628 Offset
+= CurrString
->Size
;
631 // Print useful info about this string
633 if ((StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) == 0) {
634 fprintf (Fptr
, " - not referenced");
637 if (CurrString
->Flags
& STRING_FLAGS_UNDEFINED
) {
638 fprintf (Fptr
, " - not defined for this language");
639 } else if (StrCmp (CurrString
->LanguageName
, Lang
->LanguageName
) != 0) {
642 " - not defined for this language -- using secondary language %s definition",
643 CurrString
->LanguageName
645 fprintf ( Fptr
, "%s", Line
);
648 fprintf (Fptr
, "\n");
651 // For unreferenced string identifiers, print a message that they are not referenced anywhere
653 while (StringIndex
< mDBData
.NumStringIdentifiers
) {
654 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
655 if (StringIdentifier
!= NULL
) {
656 AsciiSPrint (Line
, sizeof(Line
), " // %s not referenced\n", StringIdentifier
->StringName
);
657 fprintf (Fptr
, "%s", Line
);
664 // PASS 3: write the strings themselves.
665 // Keep track of how many bytes we write per line because some editors
666 // (Visual Studio for instance) can't handle too long of lines.
668 Offset
= sizeof (StringPack
) + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
);
669 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
670 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
671 if (StringIdentifier
== NULL
) {
672 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
676 AsciiSPrint (Line
, sizeof(Line
), " // string %s offset 0x%08X\n ", StringIdentifier
->StringName
, Offset
);
677 fprintf (Fptr
, "%s", Line
);
679 // For the first string (language name), we print out the "spacat" if they
680 // requested it. We set LangName to point to the proper language name string above.
682 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
683 TempStringPtr
= LangName
;
686 // Find a matching string if this string identifier was referenced
689 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
690 CurrString
= StringDBFindString (
692 StringIdentifier
->StringName
,
697 if (NULL
== CurrString
) {
698 CurrString
= StringDBFindString (
700 StringIdentifier
->StringName
,
708 if (CurrString
== NULL
) {
709 CurrString
= &EmptyString
;
712 TempStringPtr
= CurrString
->Str
;
716 for (TempIndex
= 0; TempStringPtr
[TempIndex
] != 0; TempIndex
++) {
720 (UINT32
) TempStringPtr
[TempIndex
] & 0xFF,
721 (UINT32
) ((TempStringPtr
[TempIndex
] >> 8) & 0xFF)
726 // Let's say we only allow 14 per line
728 if (BytesThisLine
> 14) {
729 fprintf (Fptr
, "\n ");
734 // Print NULL WCHAR at the end of this string.
736 fprintf (Fptr
, "0x00, 0x00,\n");
740 // Sanity check the offset. Make sure our running offset is what we put in the
741 // string pack header.
743 if (StringPack
.Header
.Length
!= Offset
) {
749 "stringpack size 0x%X does not match final size 0x%X",
750 StringPack
.Header
.Length
,
756 // Print terminator string pack, closing brace and close the file.
757 // The size of 0 triggers to the consumer that this is the end.
759 memset ((char *) &StringPack
, 0, sizeof (EFI_HII_STRING_PACK
));
760 StringPack
.Header
.Type
= EFI_HII_STRING
;
761 Ptr
= (UINT8
*) &StringPack
;
762 fprintf (Fptr
, "\n // strings terminator pack");
763 for (TempIndex
= 0; TempIndex
< sizeof (StringPack
); TempIndex
++, Ptr
++) {
764 if ((TempIndex
& 0x0F) == 0) {
765 fprintf (Fptr
, "\n ");
768 fprintf (Fptr
, "0x%02X, ", (UINT32
) *Ptr
);
771 fprintf (Fptr
, "\n};\n");
773 return STATUS_SUCCESS
;
776 /*****************************************************************************/
782 Dump the #define string names
786 FileName - name of the output file to write
787 BaseName - used for the protection #ifndef/#endif
795 StringDBDumpStringDefines (
801 STRING_IDENTIFIER
*Identifier
;
802 CHAR8 CopyBaseName
[100];
805 const CHAR8
*StrDefHeader
[] = {
806 "#ifndef _%s_STRINGS_DEFINE_H_\n",
807 "#define _%s_STRINGS_DEFINE_H_\n\n",
811 if ((Fptr
= fopen (FileName
, "w")) == NULL
) {
812 Error (NULL
, 0, 0, FileName
, "failed to open output string defines file");
816 // Get the base source filename and convert to uppercase.
818 if (sizeof (CopyBaseName
) <= strlen (BaseName
) + 1) {
819 Error (NULL
, 0, 0, "application error", "StringDBDumpStringDefines() string length insufficient");
823 strcpy (CopyBaseName
, BaseName
);
824 for (Index
= 0; CopyBaseName
[Index
] != 0; Index
++) {
825 if (islower (CopyBaseName
[Index
])) {
826 CopyBaseName
[Index
] = (INT8
) toupper (CopyBaseName
[Index
]);
830 // Assign index values to the string identifiers
832 StringDBAssignStringIndexes ();
834 // Write the standard header to the output file, and then the
835 // protective #ifndef.
837 StringDBWriteStandardFileHeader (Fptr
);
838 for (Index
= 0; StrDefHeader
[Index
] != NULL
; Index
++) {
839 fprintf (Fptr
, StrDefHeader
[Index
], CopyBaseName
);
842 // Print all the #defines for the string identifiers. Print identifiers
843 // whose names start with '$' as comments. Add comments for string
844 // identifiers not used as well.
846 Identifier
= mDBData
.StringIdentifier
;
847 while (Identifier
!= NULL
) {
848 if (Identifier
->StringName
[0] == L
'$') {
849 fprintf (Fptr
, "// ");
852 if (Identifier
->Flags
& STRING_FLAGS_REFERENCED
) {
853 AsciiSPrint (Line
, sizeof(Line
), "#define %-40s 0x%04X\n", Identifier
->StringName
, Identifier
->Index
);
854 fprintf (Fptr
, "%s", Line
);
856 AsciiSPrint (Line
, sizeof(Line
), "//#define %-40s 0x%04X // not referenced\n", Identifier
->StringName
, Identifier
->Index
);
857 fprintf (Fptr
, "%s", Line
);
860 Identifier
= Identifier
->Next
;
863 fprintf (Fptr
, "\n#endif\n");
865 return STATUS_SUCCESS
;
868 /*****************************************************************************/
874 Add a string identifier to the database.
878 StringName - name of the string identifier. For example "STR_MY_STRING"
879 NewId - if an ID has been assigned
880 Flags - characteristics for the identifier
888 StringDBAddStringIdentifier (
894 STRING_IDENTIFIER
*StringIdentifier
;
897 // If it was already used for some other language, then we don't
898 // need to add it. But set it to the current string identifier.
899 // The referenced bit is sticky.
901 Status
= STATUS_SUCCESS
;
902 StringIdentifier
= StringDBFindStringIdentifierByName (StringName
);
903 if (StringIdentifier
!= NULL
) {
904 if (Flags
& STRING_FLAGS_REFERENCED
) {
905 StringIdentifier
->Flags
|= STRING_FLAGS_REFERENCED
;
908 mDBData
.CurrentStringIdentifier
= StringIdentifier
;
909 *NewId
= (UINT16
) StringIdentifier
->Index
;
913 StringIdentifier
= (STRING_IDENTIFIER
*) MALLOC (sizeof (STRING_IDENTIFIER
));
914 if (StringIdentifier
== NULL
) {
915 Error (NULL
, 0, 0, NULL
, "memory allocation error");
919 memset ((char *) StringIdentifier
, 0, sizeof (STRING_IDENTIFIER
));
920 StringIdentifier
->StringName
= (WCHAR
*) malloc ((StrLen (StringName
) + 1) * sizeof (WCHAR
));
921 if (StringIdentifier
->StringName
== NULL
) {
922 Error (NULL
, 0, 0, NULL
, "memory allocation error");
926 StrCpy (StringIdentifier
->StringName
, StringName
);
927 if (*NewId
!= STRING_ID_INVALID
) {
928 StringIdentifier
->Index
= *NewId
;
929 StringIdentifier
->Flags
|= STRING_FLAGS_INDEX_ASSIGNED
;
930 if (mDBData
.NumStringIdentifiers
<= StringIdentifier
->Index
) {
931 mDBData
.NumStringIdentifiers
= StringIdentifier
->Index
+ 1;
934 StringIdentifier
->Index
= mDBData
.NumStringIdentifiers
++;
937 StringIdentifier
->Flags
|= Flags
;
939 // Add it to our list of string identifiers
941 if (mDBData
.StringIdentifier
== NULL
) {
942 mDBData
.StringIdentifier
= StringIdentifier
;
944 mDBData
.LastStringIdentifier
->Next
= StringIdentifier
;
947 mDBData
.LastStringIdentifier
= StringIdentifier
;
948 mDBData
.CurrentStringIdentifier
= StringIdentifier
;
949 *NewId
= (UINT16
) StringIdentifier
->Index
;
953 /*****************************************************************************/
959 Add a new string to the database.
963 LanguageName - "eng" or "spa" language name
964 StringName - "STR_MY_TEXT" string name
965 Scope - from the #scope statements in the string file
966 Format - if we should format the string
967 Flags - characteristic flags for the string
975 Several of the fields can be "inherited" from the previous calls to
976 our database functions. For example, if scope is NULL here, then
977 we'll use the previous setting.
994 WCHAR TempLangName
[4];
995 STRING_IDENTIFIER
*StringIdentifier
;
998 // Check that language name is exactly 3 characters, or emit an error.
999 // Truncate at 3 if it's longer, or make it 3 if it's shorter.
1001 if (LanguageName
!= NULL
) {
1002 Size
= StrLen (LanguageName
);
1004 ParserError (0, "invalid length for language name", "%S", LanguageName
);
1006 LanguageName
[3] = 0;
1009 // Make a local copy of the language name string, and extend to
1010 // 3 characters since we make assumptions elsewhere in this program
1013 StrCpy (TempLangName
, LanguageName
);
1014 for (; Size
< 3; Size
++) {
1015 TempLangName
[Size
] = L
'?';
1018 TempLangName
[4] = 0;
1019 LanguageName
= TempLangName
;
1024 // If they specified a language, make sure they've defined it already
1025 // via a #langdef statement. Otherwise use the current default language.
1027 if (LanguageName
!= NULL
) {
1028 Lang
= StringDBFindLanguageList (LanguageName
);
1030 ParserError (0, "language not defined", "%S", LanguageName
);
1031 return STATUS_ERROR
;
1033 StringDBSetCurrentLanguage (LanguageName
);
1036 Lang
= mDBData
.CurrentLanguage
;
1039 // Have to call SetLanguage() first
1041 ParserError (0, "no language defined", "%S", StringName
);
1042 return STATUS_ERROR
;
1046 // If they didn't define a string identifier, use the last string identifier
1049 if (StringName
== NULL
) {
1050 StringName
= mDBData
.CurrentStringIdentifier
->StringName
;
1051 if (StringName
== NULL
) {
1052 ParserError (0, "no string identifier previously specified", NULL
);
1053 return STATUS_ERROR
;
1057 // If scope was not specified, use the default setting
1059 if (Scope
!= NULL
) {
1060 Scope
= DuplicateString (Scope
);
1062 Scope
= DuplicateString (mDBData
.CurrentScope
);
1065 // printf ("Adding string: %S.%S.%S\n", Lang->LanguageName, StringName, Scope);
1067 // Check for duplicates for this Language.StringName.Scope. Allow multiple
1068 // definitions of the language name and printable language name, since the
1069 // user does not specifically define them.
1071 if (StringDBFindString (Lang
->LanguageName
, StringName
, Scope
, NULL
, NULL
) != NULL
) {
1072 if ((StrCmp (StringName
, LANGUAGE_NAME_STRING_NAME
) == 0) &&
1073 (StrCmp (StringName
, PRINTABLE_LANGUAGE_NAME_STRING_NAME
) == 0)
1077 "string multiply defined",
1078 "Language.Name.Scope = %S.%S.%S",
1083 return STATUS_ERROR
;
1087 StringIndex
= STRING_ID_INVALID
;
1088 if (StringDBAddStringIdentifier (StringName
, &StringIndex
, Flags
) != STATUS_SUCCESS
) {
1089 return STATUS_ERROR
;
1092 StringIdentifier
= StringDBFindStringIdentifierByName (StringName
);
1094 // Add this string to the end of the strings for this language.
1096 Str
= (STRING_LIST
*) malloc (sizeof (STRING_LIST
));
1098 Error (NULL
, 0, 0, NULL
, "memory allocation error");
1099 return STATUS_ERROR
;
1102 memset ((char *) Str
, 0, sizeof (STRING_LIST
));
1103 Size
= (StrLen (String
) + 1) * sizeof (WCHAR
);
1106 Str
->StringName
= StringIdentifier
->StringName
;
1107 Str
->LanguageName
= DuplicateString (LanguageName
);
1108 Str
->Str
= (WCHAR
*) MALLOC (Size
);
1109 if (Str
->Str
== NULL
) {
1110 Error (NULL
, 0, 0, NULL
, "memory allocation error");
1111 return STATUS_ERROR
;
1114 // If not formatting, just copy the string.
1116 StrCpy (Str
->Str
, String
);
1118 StringDBFormatString (Str
->Str
);
1121 // Size may change after formatting. We set the size to
1122 // the actual size of the string, including the null for
1123 // easier processing later.
1125 Str
->Size
= (StrLen (Str
->Str
) + 1) * sizeof (WCHAR
);
1126 if (Lang
->String
== NULL
) {
1129 Lang
->LastString
->Next
= Str
;
1132 Lang
->LastString
= Str
;
1133 return STATUS_SUCCESS
;
1136 /*****************************************************************************/
1140 Routine Description:
1142 Given a language name, see if a language list for it has been defined
1146 LanguageName - like "eng"
1150 A pointer to the language list
1155 StringDBFindLanguageList (
1159 LANGUAGE_LIST
*Lang
;
1161 Lang
= mDBData
.LanguageList
;
1162 while (Lang
!= NULL
) {
1163 if (StrCmp (LanguageName
, Lang
->LanguageName
) == 0) {
1173 /*****************************************************************************/
1175 StringDBSetCurrentLanguage (
1179 LANGUAGE_LIST
*Lang
;
1181 Lang
= StringDBFindLanguageList (LanguageName
);
1183 ParserError (0, "language not previously defined", "%S", LanguageName
);
1184 return STATUS_ERROR
;
1187 mDBData
.CurrentLanguage
= Lang
;
1188 return STATUS_SUCCESS
;
1191 /*****************************************************************************/
1193 StringDBAddLanguage (
1194 WCHAR
*LanguageName
,
1195 WCHAR
*PrintableLanguageName
1198 LANGUAGE_LIST
*Lang
;
1200 // Check for redefinitions
1202 Lang
= StringDBFindLanguageList (LanguageName
);
1205 // Better be the same printable name
1207 if (StrCmp (PrintableLanguageName
, Lang
->PrintableLanguageName
) != 0) {
1210 "language redefinition",
1213 Lang
->PrintableLanguageName
,
1215 PrintableLanguageName
1217 return STATUS_ERROR
;
1220 // ParserWarning (0, "benign language redefinition", "%S", PrintableLanguageName);
1221 // return STATUS_WARNING;
1226 // Allocate memory to keep track of this new language
1228 Lang
= (LANGUAGE_LIST
*) malloc (sizeof (LANGUAGE_LIST
));
1230 Error (NULL
, 0, 0, NULL
, "memory allocation error");
1231 return STATUS_ERROR
;
1234 memset ((char *) Lang
, 0, sizeof (LANGUAGE_LIST
));
1236 // Save the language name, then allocate memory to save the
1237 // printable language name
1239 StrCpy (Lang
->LanguageName
, LanguageName
);
1240 Lang
->PrintableLanguageName
= (WCHAR
*) malloc ((StrLen (PrintableLanguageName
) + 1) * sizeof (WCHAR
));
1241 if (Lang
->PrintableLanguageName
== NULL
) {
1242 Error (NULL
, 0, 0, NULL
, "memory allocation error");
1243 return STATUS_ERROR
;
1246 StrCpy (Lang
->PrintableLanguageName
, PrintableLanguageName
);
1248 if (mDBData
.LanguageList
== NULL
) {
1249 mDBData
.LanguageList
= Lang
;
1251 mDBData
.LastLanguageList
->Next
= Lang
;
1254 mDBData
.LastLanguageList
= Lang
;
1257 // Default is to make our active language this new one
1259 StringDBSetCurrentLanguage (LanguageName
);
1261 // The first two strings for any language are the language name,
1262 // followed by the printable language name. Add them and set them
1263 // to referenced so they never get stripped out.
1267 LANGUAGE_NAME_STRING_NAME
,
1271 STRING_FLAGS_REFERENCED
1275 PRINTABLE_LANGUAGE_NAME_STRING_NAME
,
1277 PrintableLanguageName
,
1279 STRING_FLAGS_REFERENCED
1281 return STATUS_SUCCESS
;
1284 /*****************************************************************************/
1287 StringDBFindStringIdentifierByName (
1291 STRING_IDENTIFIER
*Identifier
;
1293 Identifier
= mDBData
.StringIdentifier
;
1294 while (Identifier
!= NULL
) {
1295 if (StrCmp (StringName
, Identifier
->StringName
) == 0) {
1299 Identifier
= Identifier
->Next
;
1307 StringDBFindStringIdentifierByIndex (
1311 STRING_IDENTIFIER
*Identifier
;
1313 Identifier
= mDBData
.StringIdentifier
;
1314 while (Identifier
!= NULL
) {
1315 if (Identifier
->Index
== StringIndex
) {
1319 Identifier
= Identifier
->Next
;
1325 /*****************************************************************************/
1328 StringDBWriteStandardFileHeader (
1333 for (TempIndex
= 0; mSourceFileHeader
[TempIndex
] != NULL
; TempIndex
++) {
1334 fprintf (OutFptr
, "%s\n", mSourceFileHeader
[TempIndex
]);
1338 /*****************************************************************************/
1342 Routine Description:
1344 Given a Unicode string from an input file, reformat the string to replace
1345 backslash control sequences with the appropriate encoding.
1349 String - pointer to string to reformat
1357 StringDBFormatString (
1366 // Go through the string and process any formatting characters
1371 if (*From
== UNICODE_BACKSLASH
) {
1373 // First look for \wide and replace with the appropriate control character. Note that
1374 // when you have "define STR L"ABC"", then sizeof(ABC) is 8 because the null char is
1375 // counted. Make adjustments for this. We advance From below, so subtract 2 each time.
1377 if (StrnCmp (From
, UNICODE_WIDE_STRING
, sizeof (UNICODE_WIDE_STRING
) / sizeof (WCHAR
) - 1) == 0) {
1379 From
+= sizeof (UNICODE_WIDE_STRING
) / sizeof (WCHAR
) - 2;
1380 } else if (StrnCmp (From
, UNICODE_NARROW_STRING
, sizeof (UNICODE_NARROW_STRING
) / sizeof (WCHAR
) - 1) == 0) {
1385 From
+= sizeof (UNICODE_NARROW_STRING
) / sizeof (WCHAR
) - 2;
1386 } else if (StrnCmp (From
, UNICODE_NBR_STRING
, sizeof (UNICODE_NBR_STRING
) / sizeof (WCHAR
) - 1) == 0) {
1390 *To
= NON_BREAKING_CHAR
;
1391 From
+= sizeof (UNICODE_NBR_STRING
) / sizeof (WCHAR
) - 2;
1392 } else if (StrnCmp (From
, UNICODE_BR_STRING
, sizeof (UNICODE_BR_STRING
) / sizeof (WCHAR
) - 1) == 0) {
1394 // Found: \br -- pass through untouched
1399 // Standard one-character control sequences such as \n, \r, \\, or \x
1403 case ASCII_TO_UNICODE ('n'):
1412 case ASCII_TO_UNICODE ('r'):
1419 case UNICODE_BACKSLASH
:
1420 *To
= UNICODE_BACKSLASH
;
1426 case ASCII_TO_UNICODE ('t'):
1431 // embedded double-quote
1433 case UNICODE_DOUBLE_QUOTE
:
1434 *To
= UNICODE_DOUBLE_QUOTE
;
1438 // Hex Unicode character \x1234. We'll process up to 4 hex characters
1440 case ASCII_TO_UNICODE ('x'):
1442 for (HexNibbles
= 0; HexNibbles
< 4; HexNibbles
++) {
1443 if ((From
[1] >= UNICODE_0
) && (From
[1] <= UNICODE_9
)) {
1444 HexValue
= (HexValue
<< 4) | (From
[1] - UNICODE_0
);
1445 } else if ((From
[1] >= UNICODE_a
) && (From
[1] <= UNICODE_f
)) {
1446 HexValue
= (HexValue
<< 4) | (10 + From
[1] - UNICODE_a
);
1447 } else if ((From
[1] >= UNICODE_A
) && (From
[1] <= UNICODE_F
)) {
1448 HexValue
= (HexValue
<< 4) | (10 + From
[1] - UNICODE_A
);
1456 if (HexNibbles
== 0) {
1459 "expected at least one valid hex digit with \\x escaped character in string",
1469 *To
= UNICODE_SPACE
;
1470 ParserWarning (0, "invalid escaped character in string", "\\%C", *From
);
1485 /*****************************************************************************/
1487 StringDBReadDatabase (
1489 BOOLEAN IgnoreIfNotExist
,
1493 STRING_DB_HEADER DbHeader
;
1496 DB_DATA_ITEM_HEADER DataItemHeader
;
1498 Status
= STATUS_SUCCESS
;
1502 // fprintf (stdout, "Reading database file %s\n", DBFileName);
1505 // Try to open the input file
1507 if ((DBFptr
= fopen (DBFileName
, "rb")) == NULL
) {
1508 if (IgnoreIfNotExist
) {
1509 return STATUS_SUCCESS
;
1512 Error (NULL
, 0, 0, DBFileName
, "failed to open input database file for reading");
1513 return STATUS_ERROR
;
1516 // Read and verify the database header
1518 if (fread ((void *) &DbHeader
, sizeof (STRING_DB_HEADER
), 1, DBFptr
) != 1) {
1519 Error (NULL
, 0, 0, DBFileName
, "failed to read header from database file");
1520 Status
= STATUS_ERROR
;
1524 if (DbHeader
.Key
!= STRING_DB_KEY
) {
1525 Error (NULL
, 0, 0, DBFileName
, "invalid header in database file");
1526 Status
= STATUS_ERROR
;
1530 if ((DbHeader
.Version
& STRING_DB_MAJOR_VERSION_MASK
) != (STRING_DB_VERSION
& STRING_DB_MAJOR_VERSION_MASK
)) {
1531 Error (NULL
, 0, 0, DBFileName
, "incompatible database file version -- rebuild clean");
1532 Status
= STATUS_ERROR
;
1536 // Read remaining items
1538 while (fread (&DataItemHeader
, sizeof (DataItemHeader
), 1, DBFptr
) == 1) {
1539 switch (DataItemHeader
.DataType
) {
1540 case DB_DATA_TYPE_STRING_IDENTIFIER
:
1541 StringDBReadStringIdentifier (DBFptr
);
1544 case DB_DATA_TYPE_LANGUAGE_DEFINITION
:
1545 StringDBReadLanguageDefinition (DBFptr
);
1548 case DB_DATA_TYPE_STRING_DEFINITION
:
1549 StringDBReadString (DBFptr
);
1557 "database corrupted",
1558 "invalid data item type 0x%X at offset 0x%X",
1559 (UINT32
) DataItemHeader
.DataType
,
1560 ftell (DBFptr
) - sizeof (DataItemHeader
)
1562 Status
= STATUS_ERROR
;
1568 if (DBFptr
!= NULL
) {
1575 /*****************************************************************************/
1579 Routine Description:
1581 Write everything we know to the output database file. Write:
1584 String identifiers[]
1589 DBFileName - name of the file to write to
1590 Verbose - for debug purposes, print info messages along the way.
1598 StringDBWriteDatabase (
1603 STRING_DB_HEADER DbHeader
;
1606 LANGUAGE_LIST
*Lang
;
1607 STRING_IDENTIFIER
*StringIdentifier
;
1608 STRING_LIST
*StrList
;
1612 fprintf (stdout
, "Writing database %s\n", DBFileName
);
1615 if ((DBFptr
= fopen (DBFileName
, "wb")) == NULL
) {
1616 Error (NULL
, 0, 0, DBFileName
, "failed to open output database file for writing");
1617 return STATUS_ERROR
;
1620 // Fill in and write the database header
1622 memset (&DbHeader
, 0, sizeof (STRING_DB_HEADER
));
1623 DbHeader
.HeaderSize
= sizeof (STRING_DB_HEADER
);
1624 DbHeader
.Key
= STRING_DB_KEY
;
1625 DbHeader
.Version
= STRING_DB_VERSION
;
1627 // Count the number of languages we have
1629 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
1630 DbHeader
.NumLanguages
++;
1633 // Count up how many string identifiers we have, and total up the
1634 // size of the names plus the size of the flags field we will
1637 DbHeader
.NumStringIdenfiers
= mDBData
.NumStringIdentifiers
;
1638 StringIdentifier
= mDBData
.StringIdentifier
;
1639 for (Counter
= 0; Counter
< mDBData
.NumStringIdentifiers
; Counter
++) {
1640 StrLength
= StrLen (StringIdentifier
->StringName
) + 1;
1641 DbHeader
.StringIdentifiersSize
+= StrLength
* sizeof (WCHAR
) + sizeof (StringIdentifier
->Flags
);
1642 StringIdentifier
= StringIdentifier
->Next
;
1648 fwrite (&DbHeader
, sizeof (STRING_DB_HEADER
), 1, DBFptr
);
1650 fprintf (stdout
, " Number of string identifiers 0x%04X\n", DbHeader
.NumStringIdenfiers
);
1651 fprintf (stdout
, " Number of languages %d\n", DbHeader
.NumLanguages
);
1654 // Write the string identifiers
1656 for (StringIdentifier
= mDBData
.StringIdentifier
; StringIdentifier
!= NULL
; StringIdentifier
= StringIdentifier
->Next
) {
1657 StringDBWriteStringIdentifier (
1659 (UINT16
) StringIdentifier
->Index
,
1660 StringIdentifier
->Flags
,
1661 StringIdentifier
->StringName
1665 // Now write all the strings for each language
1667 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
1668 StringDBWriteLanguageDefinition (DBFptr
, Lang
->LanguageName
, Lang
->PrintableLanguageName
);
1669 for (StrList
= Lang
->String
; StrList
!= NULL
; StrList
= StrList
->Next
) {
1670 StringDBWriteString (
1674 StrList
->StringName
,
1682 return STATUS_SUCCESS
;
1686 StringDBSetStringReferenced (
1687 CHAR8
*StringIdentifierName
,
1688 BOOLEAN IgnoreNotFound
1691 STRING_IDENTIFIER
*Id
;
1695 // See if it's already been defined.
1697 Status
= STATUS_SUCCESS
;
1698 WName
= (WCHAR
*) malloc ((strlen (StringIdentifierName
) + 1) * sizeof (WCHAR
));
1699 UnicodeSPrint (WName
, (strlen (StringIdentifierName
) + 1) * sizeof (WCHAR
), L
"%a", StringIdentifierName
);
1700 Id
= StringDBFindStringIdentifierByName (WName
);
1702 Id
->Flags
|= STRING_FLAGS_REFERENCED
;
1704 if (IgnoreNotFound
== 0) {
1705 ParserWarning (0, StringIdentifierName
, "string identifier not found in database");
1706 Status
= STATUS_WARNING
;
1714 /*****************************************************************************/
1718 Routine Description:
1720 Dump the contents of a database to an output unicode file.
1724 DBFileName - name of the pre-existing database file to read
1725 OutputFileName - name of the file to dump the database contents to
1726 Verbose - for printing of additional info useful for debugging
1734 There's some issue with the unicode printing routines. Therefore to
1735 write to the output file properly, open it as binary and use fwrite.
1736 Ideally we could open it with just L"w" and use fwprintf().
1740 StringDBDumpDatabase (
1742 CHAR8
*OutputFileName
,
1746 LANGUAGE_LIST
*Lang
;
1747 STRING_IDENTIFIER
*StringIdentifier
;
1748 STRING_LIST
*StrList
;
1755 // This function assumes the database has already been read, and
1756 // we're just dumping our internal data structures to a unicode file.
1759 fprintf (stdout
, "Dumping database file %s\n", DBFileName
);
1762 OutFptr
= fopen (OutputFileName
, "wb");
1763 if (OutFptr
== NULL
) {
1764 Error (NULL
, 0, 0, OutputFileName
, "failed to open output file for writing");
1765 return STATUS_ERROR
;
1768 WChar
= UNICODE_FILE_START
;
1769 fwrite (&WChar
, sizeof (WCHAR
), 1, OutFptr
);
1770 CrLf
[1] = UNICODE_LF
;
1771 CrLf
[0] = UNICODE_CR
;
1773 // The default control character is '/'. Make it '#' by writing
1774 // "/=#" to the output file.
1776 UnicodeSPrint (Line
, sizeof(Line
), L
"/=#");
1777 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1778 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1779 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1781 // Dump all the string identifiers and their values
1783 StringDBAssignStringIndexes ();
1784 for (StringIdentifier
= mDBData
.StringIdentifier
; StringIdentifier
!= NULL
; StringIdentifier
= StringIdentifier
->Next
) {
1786 // Write the "#define " string
1788 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
1791 sizeof(Line
), L
"%s %-60.60s 0x%04X",
1793 StringIdentifier
->StringName
,
1794 StringIdentifier
->Index
1799 sizeof(Line
), L
"%s %-60.60s 0x%04X // NOT REFERENCED",
1801 StringIdentifier
->StringName
,
1802 StringIdentifier
->Index
1806 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1807 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1810 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1812 // Now write all the strings for each language.
1814 WChar
= UNICODE_DOUBLE_QUOTE
;
1816 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
1817 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1818 UnicodeSPrint (Line
, sizeof(Line
), L
"#langdef %s \"%s\"", Lang
->LanguageName
, Lang
->PrintableLanguageName
);
1819 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1820 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1821 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1823 // Now the strings (in double-quotes) for this language. Write
1824 // #string STR_NAME #language eng "string"
1826 for (StrList
= Lang
->String
; StrList
!= NULL
; StrList
= StrList
->Next
) {
1828 // Print the internal flags for debug
1830 UnicodeSPrint (Line
, sizeof(Line
), L
"// flags=0x%02X", (UINT32
) StrList
->Flags
);
1831 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1832 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1834 // Print the scope if changed
1836 if ((Scope
== NULL
) || (StrCmp (Scope
, StrList
->Scope
) != 0)) {
1837 UnicodeSPrint (Line
, sizeof(Line
), L
"#scope %s", StrList
->Scope
);
1838 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1839 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1840 Scope
= StrList
->Scope
;
1845 sizeof(Line
), L
"#string %-50.50s #language %s \"",
1846 StrList
->StringName
,
1849 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1850 fwrite (StrList
->Str
, StrList
->Size
- sizeof (WCHAR
), 1, OutFptr
);
1851 UnicodeSPrint (Line
, sizeof(Line
), L
"\"");
1852 fwrite (Line
, StrLen (Line
) * sizeof (WCHAR
), 1, OutFptr
);
1853 fwrite (&CrLf
, sizeof (CrLf
), 1, OutFptr
);
1858 return STATUS_SUCCESS
;
1861 /*****************************************************************************/
1865 Routine Description:
1867 Given a primary language, a string identifier number, and a list of
1868 languages, find a secondary string.
1872 LanguageName - primary language, like "spa"
1873 StringId - string index value
1874 LanguageList - linked list of "eng", "spa+cat",...
1878 Pointer to a secondary string if found. NULL otherwise.
1882 Given: LanguageName "spa" and LanguageList "spa+cat", match the
1883 "spa" and extract the "cat" and see if there is a string defined
1889 StringDBWriteStringIdentifier (
1893 WCHAR
*IdentifierName
1896 DB_DATA_ITEM_HEADER Hdr
;
1897 memset (&Hdr
, 0, sizeof (DB_DATA_ITEM_HEADER
));
1898 Hdr
.DataType
= DB_DATA_TYPE_STRING_IDENTIFIER
;
1899 if (fwrite (&Hdr
, sizeof (DB_DATA_ITEM_HEADER
), 1, DBFptr
) != 1) {
1900 Error (NULL
, 0, 0, "failed to write string to output database file", NULL
);
1901 return STATUS_ERROR
;
1904 if (fwrite (&StringId
, sizeof (StringId
), 1, DBFptr
) != 1) {
1905 Error (NULL
, 0, 0, "failed to write StringId to output database", NULL
);
1906 return STATUS_ERROR
;
1909 if (fwrite (&Flags
, sizeof (Flags
), 1, DBFptr
) != 1) {
1910 Error (NULL
, 0, 0, "failed to write StringId flags to output database", NULL
);
1911 return STATUS_ERROR
;
1914 if (StringDBWriteGenericString (DBFptr
, IdentifierName
) != STATUS_SUCCESS
) {
1915 return STATUS_ERROR
;
1918 return STATUS_SUCCESS
;
1923 StringDBReadStringIdentifier (
1927 WCHAR
*IdentifierName
;
1932 if (fread (&StringId
, sizeof (StringId
), 1, DBFptr
) != 1) {
1933 Error (NULL
, 0, 0, "failed to read StringId from database", NULL
);
1934 return STATUS_ERROR
;
1937 if (fread (&Flags
, sizeof (Flags
), 1, DBFptr
) != 1) {
1938 Error (NULL
, 0, 0, "failed to read StringId flags from database", NULL
);
1939 return STATUS_ERROR
;
1942 if (StringDBReadGenericString (DBFptr
, &Size
, &IdentifierName
) != STATUS_SUCCESS
) {
1943 return STATUS_ERROR
;
1946 StringDBAddStringIdentifier (IdentifierName
, &StringId
, Flags
);
1948 // printf ("STRID: 0x%04X %S\n", (UINT32)StringId, IdentifierName);
1950 FREE (IdentifierName
);
1951 return STATUS_SUCCESS
;
1956 StringDBWriteString (
1965 DB_DATA_ITEM_HEADER Hdr
;
1966 memset (&Hdr
, 0, sizeof (DB_DATA_ITEM_HEADER
));
1967 Hdr
.DataType
= DB_DATA_TYPE_STRING_DEFINITION
;
1968 if (fwrite (&Hdr
, sizeof (DB_DATA_ITEM_HEADER
), 1, DBFptr
) != 1) {
1969 Error (NULL
, 0, 0, "failed to write string header to output database file", NULL
);
1970 return STATUS_ERROR
;
1973 if (fwrite (&Flags
, sizeof (Flags
), 1, DBFptr
) != 1) {
1974 Error (NULL
, 0, 0, "failed to write string flags to output database", NULL
);
1975 return STATUS_ERROR
;
1978 if (StringDBWriteGenericString (DBFptr
, Language
) != STATUS_SUCCESS
) {
1979 return STATUS_ERROR
;
1982 if (StringDBWriteGenericString (DBFptr
, StringName
) != STATUS_SUCCESS
) {
1983 return STATUS_ERROR
;
1986 if (StringDBWriteGenericString (DBFptr
, Scope
) != STATUS_SUCCESS
) {
1987 return STATUS_ERROR
;
1990 if (StringDBWriteGenericString (DBFptr
, Str
) != STATUS_SUCCESS
) {
1991 return STATUS_ERROR
;
1994 // printf ("DBWriteString: %S.%S.%S\n", Language, StringName, Scope);
1996 return STATUS_SUCCESS
;
2001 StringDBReadString (
2012 if (fread (&Flags
, sizeof (Flags
), 1, DBFptr
) != 1) {
2013 Error (NULL
, 0, 0, "failed to read string flags from database", NULL
);
2014 return STATUS_ERROR
;
2017 if (StringDBReadGenericString (DBFptr
, &Size
, &Language
) != STATUS_SUCCESS
) {
2018 return STATUS_ERROR
;
2021 if (StringDBReadGenericString (DBFptr
, &Size
, &StringName
) != STATUS_SUCCESS
) {
2022 return STATUS_ERROR
;
2025 if (StringDBReadGenericString (DBFptr
, &Size
, &Scope
) != STATUS_SUCCESS
) {
2026 return STATUS_ERROR
;
2029 if (StringDBReadGenericString (DBFptr
, &Size
, &Str
) != STATUS_SUCCESS
) {
2030 return STATUS_ERROR
;
2033 // If the first or second string (language name and printable language name),
2034 // then skip them. They're added via language definitions data items in
2037 if (StringName
[0] != L
'$') {
2038 StringDBAddString (Language
, StringName
, Scope
, Str
, FALSE
, Flags
);
2041 // printf ("DBReadString: %S.%S.%S\n", Language, StringName, Scope);
2049 if (Scope
!= NULL
) {
2053 return STATUS_SUCCESS
;
2058 StringDBWriteLanguageDefinition (
2060 WCHAR
*LanguageName
,
2061 WCHAR
*PrintableLanguageName
2064 DB_DATA_ITEM_HEADER Hdr
;
2065 memset (&Hdr
, 0, sizeof (DB_DATA_ITEM_HEADER
));
2066 Hdr
.DataType
= DB_DATA_TYPE_LANGUAGE_DEFINITION
;
2067 if (fwrite (&Hdr
, sizeof (DB_DATA_ITEM_HEADER
), 1, DBFptr
) != 1) {
2068 Error (NULL
, 0, 0, "failed to write string to output database file", NULL
);
2069 return STATUS_ERROR
;
2072 if (StringDBWriteGenericString (DBFptr
, LanguageName
) != STATUS_SUCCESS
) {
2073 return STATUS_ERROR
;
2076 if (StringDBWriteGenericString (DBFptr
, PrintableLanguageName
) != STATUS_SUCCESS
) {
2077 return STATUS_ERROR
;
2080 return STATUS_SUCCESS
;
2085 StringDBReadLanguageDefinition (
2089 WCHAR
*LanguageName
;
2090 WCHAR
*PrintableLanguageName
;
2094 if (StringDBReadGenericString (DBFptr
, &Size
, &LanguageName
) != STATUS_SUCCESS
) {
2095 return STATUS_ERROR
;
2098 if (StringDBReadGenericString (DBFptr
, &Size
, &PrintableLanguageName
) != STATUS_SUCCESS
) {
2099 return STATUS_ERROR
;
2102 // printf("LANG: %S %S\n", LanguageName, PrintableLanguageName);
2104 Status
= StringDBAddLanguage (LanguageName
, PrintableLanguageName
);
2105 FREE (LanguageName
);
2106 FREE (PrintableLanguageName
);
2110 // All unicode strings in the database consist of a UINT16 length
2111 // field, followed by the string itself. This routine reads one
2112 // of those and returns the info.
2116 StringDBReadGenericString (
2126 if (fread (&LSize
, sizeof (UINT16
), 1, DBFptr
) != 1) {
2127 Error (NULL
, 0, 0, "failed to read a string length field from the database", NULL
);
2128 return STATUS_ERROR
;
2131 if (fread (&Flags
, sizeof (UINT16
), 1, DBFptr
) != 1) {
2132 Error (NULL
, 0, 0, "failed to read a string flags field from the database", NULL
);
2133 return STATUS_ERROR
;
2136 LStr
= MALLOC (LSize
);
2138 Error (__FILE__
, __LINE__
, 0, "memory allocation failed reading the database", NULL
);
2139 return STATUS_ERROR
;
2142 if (fread (LStr
, sizeof (WCHAR
), (UINT32
) LSize
/ sizeof (WCHAR
), DBFptr
) != (UINT32
) LSize
/ sizeof (WCHAR
)) {
2143 Error (NULL
, 0, 0, "failed to read string from database", NULL
);
2144 Error (NULL
, 0, 0, "database read failure", "offset 0x%X", ftell (DBFptr
));
2146 return STATUS_ERROR
;
2149 // printf ("DBR: %S\n", LStr);
2151 // If the flags field indicated we were asked to write a NULL string, then
2152 // return them a NULL pointer.
2154 if (Flags
& STRING_FLAGS_UNDEFINED
) {
2162 return STATUS_SUCCESS
;
2167 StringDBWriteGenericString (
2174 WCHAR ZeroString
[1];
2176 // Strings in the database consist of a size UINT16 followed
2177 // by the string itself.
2182 Size
= sizeof (ZeroString
);
2183 Flags
= STRING_FLAGS_UNDEFINED
;
2186 Size
= (UINT16
) ((StrLen (Str
) + 1) * sizeof (WCHAR
));
2189 if (fwrite (&Size
, sizeof (UINT16
), 1, DBFptr
) != 1) {
2190 Error (NULL
, 0, 0, "failed to write string size to database", NULL
);
2191 return STATUS_ERROR
;
2194 if (fwrite (&Flags
, sizeof (UINT16
), 1, DBFptr
) != 1) {
2195 Error (NULL
, 0, 0, "failed to write string flags to database", NULL
);
2196 return STATUS_ERROR
;
2199 if (fwrite (Str
, sizeof (WCHAR
), Size
/ sizeof (WCHAR
), DBFptr
) != Size
/ sizeof (WCHAR
)) {
2200 Error (NULL
, 0, 0, "failed to write string to database", NULL
);
2201 return STATUS_ERROR
;
2204 return STATUS_SUCCESS
;
2209 StringDBFindString (
2210 WCHAR
*LanguageName
,
2213 WCHAR_STRING_LIST
*LanguagesOfInterest
,
2214 WCHAR_MATCHING_STRING_LIST
*IndirectionList
2217 LANGUAGE_LIST
*Lang
;
2218 STRING_LIST
*CurrString
;
2219 WCHAR_MATCHING_STRING_LIST
*IndListPtr
;
2220 WCHAR TempLangName
[LANGUAGE_IDENTIFIER_NAME_LEN
+ 1];
2224 // If we were given an indirection list, then see if one was specified for this
2225 // string identifier. That is to say, if the indirection says "STR_ID_MY_FAVORITE MyScope",
2226 // then if this string name matches one in the list, then do a lookup with the
2227 // specified scope and return that value.
2229 if (IndirectionList
!= NULL
) {
2230 for (IndListPtr
= IndirectionList
; IndListPtr
!= NULL
; IndListPtr
= IndListPtr
->Next
) {
2231 if (StrCmp (StringName
, IndListPtr
->Str1
) == 0) {
2232 CurrString
= StringDBFindString (LanguageName
, StringName
, IndListPtr
->Str2
, LanguagesOfInterest
, NULL
);
2233 if (CurrString
!= NULL
) {
2240 // First look for exact match language.stringname
2242 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
2243 if (StrCmp (LanguageName
, Lang
->LanguageName
) == 0) {
2245 // Found language match. Try to find string name match
2247 for (CurrString
= Lang
->String
; CurrString
!= NULL
; CurrString
= CurrString
->Next
) {
2248 if (StrCmp (StringName
, CurrString
->StringName
) == 0) {
2250 // Found a string name match. See if we're supposed to find
2253 if (Scope
!= NULL
) {
2254 if (StrCmp (CurrString
->Scope
, Scope
) == 0) {
2265 // If we got here, then we didn't find a match. Look for secondary string
2266 // matches. That is to say, if we're processing "spa", and they requested
2267 // "spa+cat", then recursively call with "cat"
2269 while (LanguagesOfInterest
!= NULL
) {
2271 // If this is the language we're looking for, then process the
2272 // languages of interest list for it.
2274 if (StrnCmp (LanguageName
, LanguagesOfInterest
->Str
, LANGUAGE_IDENTIFIER_NAME_LEN
) == 0) {
2275 WCharPtr
= LanguagesOfInterest
->Str
+ LANGUAGE_IDENTIFIER_NAME_LEN
;
2278 // Double-check the length, though it should have been checked on the
2281 if (StrLen (WCharPtr
) < LANGUAGE_IDENTIFIER_NAME_LEN
) {
2282 Error (NULL
, 0, 0, "malformed alternate language list", "%S", LanguagesOfInterest
->Str
);
2286 StrnCpy (TempLangName
, WCharPtr
, LANGUAGE_IDENTIFIER_NAME_LEN
);
2287 TempLangName
[LANGUAGE_IDENTIFIER_NAME_LEN
] = 0;
2288 CurrString
= StringDBFindString (TempLangName
, StringName
, NULL
, NULL
, IndirectionList
);
2289 if (CurrString
!= NULL
) {
2293 WCharPtr
+= LANGUAGE_IDENTIFIER_NAME_LEN
;
2297 LanguagesOfInterest
= LanguagesOfInterest
->Next
;
2309 // Free up existing scope memory.
2311 if (mDBData
.CurrentScope
!= NULL
) {
2312 FREE (mDBData
.CurrentScope
);
2315 mDBData
.CurrentScope
= DuplicateString (Scope
);
2316 return STATUS_SUCCESS
;
2319 // We typically don't assign index values to string identifiers
2320 // until we're ready to write out files. To reduce the size of
2321 // the output file, re-order the string identifiers to move any
2322 // unreferenced ones to the end. Then we'll walk the list
2323 // again to assign string indexes, keeping track of the last
2328 StringDBAssignStringIndexes (
2332 STRING_IDENTIFIER
*StrId
;
2333 STRING_IDENTIFIER
*FirstUsed
;
2334 STRING_IDENTIFIER
*LastUsed
;
2335 STRING_IDENTIFIER
*FirstUnused
;
2336 STRING_IDENTIFIER
*LastUnused
;
2338 UINT32 MaxReferenced
;
2341 // Create two lists -- used and unused. Then put them together with
2342 // the unused ones on the end.
2348 StrId
= mDBData
.StringIdentifier
;
2349 while (StrId
!= NULL
) {
2350 if ((StrId
->Flags
& STRING_FLAGS_REFERENCED
) == 0) {
2352 // Put it on the unused list
2354 if (FirstUnused
== NULL
) {
2355 FirstUnused
= StrId
;
2357 LastUnused
->Next
= StrId
;
2361 StrId
= StrId
->Next
;
2362 LastUnused
->Next
= NULL
;
2365 // Put it on the used list
2367 if (FirstUsed
== NULL
) {
2370 LastUsed
->Next
= StrId
;
2374 StrId
= StrId
->Next
;
2375 LastUsed
->Next
= NULL
;
2381 if (FirstUsed
!= NULL
) {
2382 mDBData
.StringIdentifier
= FirstUsed
;
2383 LastUsed
->Next
= FirstUnused
;
2385 mDBData
.StringIdentifier
= FirstUnused
;
2390 for (StrId
= mDBData
.StringIdentifier
; StrId
!= NULL
; StrId
= StrId
->Next
) {
2391 StrId
->Index
= Index
;
2393 if (StrId
->Flags
& STRING_FLAGS_REFERENCED
) {
2394 mDBData
.NumStringIdentifiersReferenced
= Index
;
2398 mDBData
.NumStringIdentifiers
= Index
;
2412 NewStr
= MALLOC ((StrLen (Str
) + 1) * sizeof (WCHAR
));
2413 if (NewStr
== NULL
) {
2414 Error (NULL
, 0, 0, "memory allocation failure", NULL
);
2418 StrCpy (NewStr
, Str
);
2432 Len
= strlen (Str
) + 1;
2433 NewStr
= (WCHAR
*) malloc (Len
* sizeof (WCHAR
));
2434 for (Ptr
= NewStr
; *Str
!= 0; Str
++, Ptr
++) {
2435 *Ptr
= (UINT16
) (UINT8
) *Str
;
2442 /*****************************************************************************/
2446 Routine Description:
2448 Create an HII export string pack for the strings in our database.
2452 FileName - name of the output file to write
2461 StringDBCreateHiiExportPack (
2466 LANGUAGE_LIST
*Lang
;
2467 STRING_LIST
*CurrString
;
2468 STRING_LIST EmptyString
;
2472 EFI_HII_STRING_PACK StringPack
;
2474 WCHAR ZeroString
[1];
2475 WCHAR
*TempStringPtr
;
2477 STRING_IDENTIFIER
*StringIdentifier
;
2479 if ((Fptr
= fopen (FileName
, "wb")) == NULL
) {
2480 Error (NULL
, 0, 0, FileName
, "failed to open output HII export file");
2481 return STATUS_ERROR
;
2484 // Assign index values to the string identifiers
2486 StringDBAssignStringIndexes ();
2488 // If a given string is not defined, then we'll use this one.
2490 memset (&EmptyString
, 0, sizeof (EmptyString
));
2491 EmptyString
.Size
= sizeof (ZeroString
);
2492 EmptyString
.Str
= ZeroString
;
2494 // Process each language, then each string for each langage
2497 for (Lang
= mDBData
.LanguageList
; Lang
!= NULL
; Lang
= Lang
->Next
) {
2499 // Process each string for this language. We have to make 3 passes on the strings:
2500 // Pass1: computes sizes and fill in the string pack header
2501 // Pass2: write the array of offsets
2502 // Pass3: write the strings
2505 // PASS 1: Fill in and print the HII string pack header
2507 // Compute the size for this language package and write
2508 // the header out. Each string package contains:
2510 // Offset[] -- an array of offsets to strings, of type RELOFST each
2511 // String[] -- the actual strings themselves
2513 memset ((char *) &StringPack
, 0, sizeof (EFI_HII_STRING_PACK
));
2514 StringPack
.Header
.Type
= EFI_HII_STRING
;
2515 StringPack
.NumStringPointers
= (UINT16
) mDBData
.NumStringIdentifiersReferenced
;
2516 LangName
= Lang
->LanguageName
;
2518 // First string is the language name. If we're printing all languages, then
2519 // it's just the "spa". If we were given a list of languages to print, then it's
2520 // the "spacat" string. Compute its offset and fill in
2521 // the info in the header. Since we know the language name string's length,
2522 // and the printable language name follows it, use that info to fill in the
2523 // entry for the printable language name as well.
2525 StringPack
.LanguageNameString
= (STRING_OFFSET
) (sizeof (EFI_HII_STRING_PACK
) + (mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
)));
2526 StringPack
.PrintableLanguageName
= (STRING_OFFSET
) (StringPack
.LanguageNameString
+ (StrLen (LangName
) + 1) * sizeof (WCHAR
));
2528 // Add up the size of all strings so we can fill in our header.
2531 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
2533 // For the first string (language name), we print out the "spacat" if they
2534 // requested it. We set LangName to point to the proper language name string above.
2536 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
2537 Len
+= (StrLen (LangName
) + 1) * sizeof (WCHAR
);
2540 // Find a string with this language.stringname
2542 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
2543 if (StringIdentifier
== NULL
) {
2544 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
2545 return STATUS_ERROR
;
2548 // Find a matching string if this string identifier was referenced
2550 EmptyString
.Flags
= STRING_FLAGS_UNDEFINED
;
2552 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
2553 CurrString
= StringDBFindString (
2555 StringIdentifier
->StringName
,
2557 NULL
, // LanguagesOfInterest,
2561 // IndirectionList);
2563 if (NULL
== CurrString
) {
2565 // If string for Lang->LanguageName is not found, try to get an English version
2567 CurrString
= StringDBFindString (
2569 StringIdentifier
->StringName
,
2571 NULL
, // LanguagesOfInterest,
2575 // IndirectionList);
2580 if (CurrString
== NULL
) {
2581 CurrString
= &EmptyString
;
2582 EmptyString
.Flags
|= StringIdentifier
->Flags
;
2585 Len
+= CurrString
->Size
;
2588 StringPack
.Header
.Length
= sizeof (EFI_HII_STRING_PACK
)
2589 + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
)
2592 // Write out the string pack header
2594 fwrite ((void *) &StringPack
, sizeof (StringPack
), 1, Fptr
);
2596 // PASS2 : write the offsets
2598 // Traverse the list of strings again and write the array of offsets. The
2599 // offset to the first string is the size of the string pack header
2600 // plus the size of the offsets array. The other strings follow it.
2603 Offset
= sizeof (StringPack
) + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
);
2604 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
2608 fwrite (&Offset
, sizeof (STRING_OFFSET
), 1, Fptr
);
2610 // Find the string name
2612 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
2613 if (StringIdentifier
== NULL
) {
2614 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
2615 return STATUS_ERROR
;
2618 // For the first string (language name), we print out the "spacat" if they
2619 // requested it. We set LangName to point to the proper language name string above.
2621 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
2622 Offset
+= (StrLen (LangName
) + 1) * sizeof (WCHAR
);
2623 CurrString
= StringDBFindString (
2625 StringIdentifier
->StringName
,
2632 // Find a matching string
2634 CurrString
= StringDBFindString (
2636 StringIdentifier
->StringName
,
2638 NULL
, // LanguagesOfInterest,
2642 // IndirectionList);
2644 if (NULL
== CurrString
) {
2645 CurrString
= StringDBFindString (
2647 StringIdentifier
->StringName
,
2649 NULL
, // LanguagesOfInterest,
2653 // IndirectionList);
2657 EmptyString
.LanguageName
= Lang
->LanguageName
;
2658 if (CurrString
== NULL
) {
2659 CurrString
= &EmptyString
;
2660 EmptyString
.Flags
= STRING_FLAGS_UNDEFINED
;
2661 } else if ((StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) == 0) {
2662 CurrString
= &EmptyString
;
2663 EmptyString
.Flags
= 0;
2666 Offset
+= CurrString
->Size
;
2671 // PASS 3: write the strings themselves.
2673 Offset
= sizeof (StringPack
) + mDBData
.NumStringIdentifiersReferenced
* sizeof (STRING_OFFSET
);
2674 for (StringIndex
= 0; StringIndex
< mDBData
.NumStringIdentifiersReferenced
; StringIndex
++) {
2675 StringIdentifier
= StringDBFindStringIdentifierByIndex (StringIndex
);
2676 if (StringIdentifier
== NULL
) {
2677 Error (NULL
, 0, 0, "internal error", "invalid string index 0x%X", StringIndex
);
2678 return STATUS_ERROR
;
2681 // For the first string (language name), we print out the "spacat" if they
2682 // requested it. We set LangName to point to the proper language name string above.
2684 if (StringIndex
== STRING_ID_LANGUAGE_NAME
) {
2685 TempStringPtr
= LangName
;
2688 // Find a matching string if this string identifier was referenced
2691 if (StringIdentifier
->Flags
& STRING_FLAGS_REFERENCED
) {
2692 CurrString
= StringDBFindString (
2694 StringIdentifier
->StringName
,
2696 NULL
, // LanguagesOfInterest,
2700 // IndirectionList);
2702 if (NULL
== CurrString
) {
2703 CurrString
= StringDBFindString (
2705 StringIdentifier
->StringName
,
2707 NULL
, // LanguagesOfInterest,
2711 // IndirectionList);
2716 if (CurrString
== NULL
) {
2717 CurrString
= &EmptyString
;
2720 TempStringPtr
= CurrString
->Str
;
2723 for (TempIndex
= 0; TempStringPtr
[TempIndex
] != 0; TempIndex
++) {
2724 fwrite (&TempStringPtr
[TempIndex
], sizeof (CHAR16
), 1, Fptr
);
2728 // Print NULL WCHAR at the end of this string.
2731 fwrite (&TempIndex
, sizeof (CHAR16
), 1, Fptr
);
2735 // Sanity check the offset. Make sure our running offset is what we put in the
2736 // string pack header.
2738 if (StringPack
.Header
.Length
!= Offset
) {
2743 "application error",
2744 "stringpack size 0x%X does not match final size 0x%X",
2745 StringPack
.Header
.Length
,
2751 // Print terminator string pack, closing brace and close the file.
2752 // The size of 0 triggers to the consumer that this is the end.
2754 memset ((char *) &StringPack
, 0, sizeof (EFI_HII_STRING_PACK
));
2755 StringPack
.Header
.Type
= EFI_HII_STRING
;
2756 fwrite ((void *) &StringPack
, sizeof (StringPack
), 1, Fptr
);
2758 return STATUS_SUCCESS
;