Home | History | Annotate | Download | only in StrGather
      1 /*++
      2 
      3 Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR>
      4 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
      8 
      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.
     11 
     12 Module Name:
     13 
     14   StringDB.c
     15 
     16 Abstract:
     17 
     18   String database implementation
     19 
     20 --*/
     21 
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <string.h>
     25 #include <ctype.h>  // for tolower()
     26 #include "Tiano.h"
     27 #include "EfiUtilityMsgs.h"
     28 #include "StrGather.h"
     29 #include "StringDb.h"
     30 #include "EfiInternalFormRepresentation.h"
     31 
     32 #include EFI_PROTOCOL_DEFINITION (Hii)
     33 
     34 typedef CHAR16  WCHAR;
     35 #define STRING_OFFSET RELOFST
     36 
     37 #define STRING_DB_KEY (('S' << 24) | ('D' << 16) | ('B' << 8) | 'K')
     38 //
     39 // Version supported by this tool
     40 //
     41 #define STRING_DB_VERSION             0x00010000
     42 
     43 #define STRING_DB_MAJOR_VERSION_MASK  0xFFFF0000
     44 #define STRING_DB_MINOR_VERSION_MASK  0x0000FFFF
     45 
     46 #define DEFINE_STR                    L"// #define"
     47 
     48 #define LANGUAGE_CODE_WIDTH           4
     49 //
     50 // This is the header that gets written to the top of the
     51 // output binary database file.
     52 //
     53 typedef struct {
     54   UINT32  Key;
     55   UINT32  HeaderSize;
     56   UINT32  Version;
     57   UINT32  NumStringIdenfiers;
     58   UINT32  StringIdentifiersSize;
     59   UINT32  NumLanguages;
     60 } STRING_DB_HEADER;
     61 
     62 //
     63 // When we write out data to the database, we have a UINT16 identifier, which
     64 // indicates what follows, followed by the data. Here's the structure.
     65 //
     66 typedef struct {
     67   UINT16  DataType;
     68   UINT16  Reserved;
     69 } DB_DATA_ITEM_HEADER;
     70 
     71 #define DB_DATA_TYPE_INVALID              0x0000
     72 #define DB_DATA_TYPE_STRING_IDENTIFIER    0x0001
     73 #define DB_DATA_TYPE_LANGUAGE_DEFINITION  0x0002
     74 #define DB_DATA_TYPE_STRING_DEFINITION    0x0003
     75 #define DB_DATA_TYPE_LAST                 DB_DATA_TYPE_STRING_DEFINITION
     76 
     77 //
     78 // We have to keep track of a list of languages, each of which has its own
     79 // list of strings. Define a structure to keep track of all languages and
     80 // their list of strings.
     81 //
     82 typedef struct _STRING_LIST {
     83   struct _STRING_LIST *Next;
     84   UINT32              Size;         // number of bytes in string, including null terminator
     85   WCHAR               *LanguageName;
     86   WCHAR               *StringName;  // for example STR_ID_TEXT1
     87   WCHAR               *Scope;       //
     88   WCHAR               *Str;         // the actual string
     89   UINT16              Flags;        // properties of this string (used, undefined)
     90 } STRING_LIST;
     91 
     92 typedef struct _LANGUAGE_LIST {
     93   struct _LANGUAGE_LIST *Next;
     94   WCHAR                 LanguageName[4];
     95   WCHAR                 *PrintableLanguageName;
     96   STRING_LIST           *String;
     97   STRING_LIST           *LastString;
     98 } LANGUAGE_LIST;
     99 
    100 //
    101 // We also keep track of all the string identifier names, which we assign unique
    102 // values to. Create a structure to keep track of them all.
    103 //
    104 typedef struct _STRING_IDENTIFIER {
    105   struct _STRING_IDENTIFIER *Next;
    106   UINT32                    Index;  // only need 16 bits, but makes it easier with UINT32
    107   WCHAR                     *StringName;
    108   UINT16                    Flags;  // if someone referenced it via STRING_TOKEN()
    109 } STRING_IDENTIFIER;
    110 //
    111 // Keep our globals in this structure to be as modular as possible.
    112 //
    113 typedef struct {
    114   FILE              *StringDBFptr;
    115   LANGUAGE_LIST     *LanguageList;
    116   LANGUAGE_LIST     *LastLanguageList;
    117   LANGUAGE_LIST     *CurrentLanguage;         // keep track of the last language they used
    118   STRING_IDENTIFIER *StringIdentifier;
    119   STRING_IDENTIFIER *LastStringIdentifier;
    120   UINT8             *StringDBFileName;
    121   UINT32            NumStringIdentifiers;
    122   UINT32            NumStringIdentifiersReferenced;
    123   STRING_IDENTIFIER *CurrentStringIdentifier; // keep track of the last string identifier they added
    124   WCHAR             *CurrentScope;
    125 } STRING_DB_DATA;
    126 
    127 static STRING_DB_DATA mDBData;
    128 
    129 static const char     *mSourceFileHeader[] = {
    130   "//",
    131   "//  DO NOT EDIT -- auto-generated file",
    132   "//",
    133   "//  This file is generated by the string gather utility",
    134   "//",
    135   NULL
    136 };
    137 
    138 static
    139 STRING_LIST           *
    140 StringDBFindString (
    141   WCHAR                       *LanguageName,
    142   WCHAR                       *StringName,
    143   WCHAR                       *Scope,
    144   WCHAR_STRING_LIST           *LanguagesOfInterest,
    145   WCHAR_MATCHING_STRING_LIST  *IndirectionList
    146   );
    147 
    148 static
    149 STRING_IDENTIFIER     *
    150 StringDBFindStringIdentifierByName (
    151   WCHAR *Name
    152   );
    153 
    154 static
    155 STRING_IDENTIFIER     *
    156 StringDBFindStringIdentifierByIndex (
    157   UINT32    Index
    158   );
    159 
    160 static
    161 LANGUAGE_LIST         *
    162 StringDBFindLanguageList (
    163   WCHAR *LanguageName
    164   );
    165 
    166 static
    167 void
    168 StringDBWriteStandardFileHeader (
    169   FILE *OutFptr
    170   );
    171 
    172 static
    173 WCHAR                 *
    174 AsciiToWchar (
    175   INT8 *Str
    176   );
    177 
    178 static
    179 WCHAR                 *
    180 DuplicateString (
    181   WCHAR   *Str
    182   );
    183 
    184 static
    185 STATUS
    186 StringDBWriteStringIdentifier (
    187   FILE                *DBFptr,
    188   UINT16              StringId,
    189   UINT16              Flags,
    190   WCHAR               *IdentifierName
    191   );
    192 
    193 static
    194 STATUS
    195 StringDBReadStringIdentifier (
    196   FILE                *DBFptr
    197   );
    198 
    199 static
    200 STATUS
    201 StringDBWriteLanguageDefinition (
    202   FILE            *DBFptr,
    203   WCHAR           *LanguageName,
    204   WCHAR           *PrintableLanguageName
    205   );
    206 
    207 static
    208 STATUS
    209 StringDBReadLanguageDefinition (
    210   FILE            *DBFptr
    211   );
    212 
    213 static
    214 STATUS
    215 StringDBWriteString (
    216   FILE            *DBFptr,
    217   UINT16          Flags,
    218   WCHAR           *Language,
    219   WCHAR           *StringName,
    220   WCHAR           *Scope,
    221   WCHAR           *Str
    222   );
    223 
    224 static
    225 STATUS
    226 StringDBReadString (
    227   FILE            *DBFptr
    228   );
    229 
    230 static
    231 STATUS
    232 StringDBReadGenericString (
    233   FILE      *DBFptr,
    234   UINT16    *Size,
    235   WCHAR     **Str
    236   );
    237 
    238 static
    239 STATUS
    240 StringDBWriteGenericString (
    241   FILE      *DBFptr,
    242   WCHAR     *Str
    243   );
    244 
    245 static
    246 void
    247 StringDBAssignStringIndexes (
    248   VOID
    249   );
    250 
    251 /*****************************************************************************/
    252 
    253 /*++
    254 
    255 Routine Description:
    256   Constructor function for the string database handler.
    257 
    258 Arguments:
    259   None.
    260 
    261 Returns:
    262   None.
    263 
    264 --*/
    265 void
    266 StringDBConstructor (
    267   VOID
    268   )
    269 {
    270   memset ((char *) &mDBData, 0, sizeof (STRING_DB_DATA));
    271   mDBData.CurrentScope = DuplicateString (L"NULL");
    272 }
    273 
    274 /*****************************************************************************/
    275 
    276 /*++
    277 
    278 Routine Description:
    279   Destructor function for the string database handler.
    280 
    281 Arguments:
    282   None.
    283 
    284 Returns:
    285   None.
    286 
    287 --*/
    288 void
    289 StringDBDestructor (
    290   VOID
    291   )
    292 {
    293   LANGUAGE_LIST     *NextLang;
    294   STRING_LIST       *NextStr;
    295   STRING_IDENTIFIER *NextIdentifier;
    296   //
    297   // Close the database file if it's open
    298   //
    299   if (mDBData.StringDBFptr != NULL) {
    300     fclose (mDBData.StringDBFptr);
    301     mDBData.StringDBFptr = NULL;
    302   }
    303   //
    304   // If we've allocated any strings/languages, free them up
    305   //
    306   while (mDBData.LanguageList != NULL) {
    307     NextLang = mDBData.LanguageList->Next;
    308     //
    309     // Free up all strings for this language
    310     //
    311     while (mDBData.LanguageList->String != NULL) {
    312       NextStr = mDBData.LanguageList->String->Next;
    313       FREE (mDBData.LanguageList->String->Str);
    314       FREE (mDBData.LanguageList->String);
    315       mDBData.LanguageList->String = NextStr;
    316     }
    317 
    318     FREE (mDBData.LanguageList->PrintableLanguageName);
    319     FREE (mDBData.LanguageList);
    320     mDBData.LanguageList = NextLang;
    321   }
    322   //
    323   // Free up string identifiers
    324   //
    325   while (mDBData.StringIdentifier != NULL) {
    326     NextIdentifier = mDBData.StringIdentifier->Next;
    327     FREE (mDBData.StringIdentifier->StringName);
    328     FREE (mDBData.StringIdentifier);
    329     mDBData.StringIdentifier = NextIdentifier;
    330   }
    331   //
    332   // Free the filename
    333   //
    334   if (mDBData.StringDBFileName != NULL) {
    335     FREE (mDBData.StringDBFileName);
    336     mDBData.StringDBFileName = NULL;
    337   }
    338   //
    339   // We save a copy of the scope, so free it up if we
    340   // have one.
    341   //
    342   if (mDBData.CurrentScope != NULL) {
    343     FREE (mDBData.CurrentScope);
    344     mDBData.CurrentScope = NULL;
    345   }
    346 }
    347 
    348 /*****************************************************************************/
    349 
    350 /*++
    351 
    352 Routine Description:
    353 
    354   Dump the contents of a database to an output C file.
    355 
    356 Arguments:
    357 
    358   FileName        - name of the output file to write
    359   BaseName        - used for the name of the C array defined
    360   Languages       - list of languages of interest
    361 
    362 Returns:
    363 
    364   STATUS
    365 
    366 Notes:
    367 
    368   Languages is a pointer to a linked list of languages specified on
    369   the command line. Format is "eng" and "spa+cat". For this, print
    370   the strings for eng. Print the strings for spa too, but if one is
    371   missing look for a cat string and print if it it exists.
    372 
    373 --*/
    374 STATUS
    375 StringDBDumpCStrings (
    376   INT8                        *FileName,
    377   INT8                        *BaseName,
    378   WCHAR_STRING_LIST           *LanguagesOfInterest,
    379   WCHAR_MATCHING_STRING_LIST  *IndirectionList
    380   )
    381 {
    382   FILE                        *Fptr;
    383   LANGUAGE_LIST               *Lang;
    384   STRING_LIST                 *CurrString;
    385   STRING_LIST                 EmptyString;
    386   UINT32                      Offset;
    387   UINT32                      StringIndex;
    388   UINT32                      TempIndex;
    389   UINT32                      BytesThisLine;
    390   EFI_HII_STRING_PACK_HEADER  StringPack;
    391   UINT8                       *Ptr;
    392   UINT32                      Len;
    393   WCHAR                       ZeroString[1];
    394   WCHAR_STRING_LIST           *LOIPtr;
    395   BOOLEAN                     LanguageOk;
    396   WCHAR                       *TempStringPtr;
    397   WCHAR                       *LangName;
    398   STRING_IDENTIFIER           *StringIdentifier;
    399 
    400   if ((Fptr = fopen (FileName, "w")) == NULL) {
    401     Error (NULL, 0, 0, FileName, "failed to open output C string file");
    402     return STATUS_ERROR;
    403   }
    404   //
    405   // Assign index values to the string identifiers
    406   //
    407   StringDBAssignStringIndexes ();
    408   //
    409   // Write the standard header to the output file, then the structure
    410   // definition header.
    411   //
    412   StringDBWriteStandardFileHeader (Fptr);
    413   fprintf (Fptr, "\nunsigned char %s[] = {\n", BaseName);
    414   //
    415   // If a given string is not defined, then we'll use this one.
    416   //
    417   memset (&EmptyString, 0, sizeof (EmptyString));
    418   EmptyString.Size  = sizeof (ZeroString);
    419   EmptyString.Str   = ZeroString;
    420   //
    421   // Process each language, then each string for each langage
    422   //
    423   ZeroString[0] = 0;
    424   for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
    425     //
    426     // If we have a language list, then make sure this language is in that
    427     // list.
    428     //
    429     LanguageOk  = TRUE;
    430     LangName    = Lang->LanguageName;
    431     if (LanguagesOfInterest != NULL) {
    432       LanguageOk = FALSE;
    433       for (LOIPtr = LanguagesOfInterest; LOIPtr != NULL; LOIPtr = LOIPtr->Next) {
    434         if (wcsncmp (LOIPtr->Str, Lang->LanguageName, LANGUAGE_IDENTIFIER_NAME_LEN) == 0) {
    435           LangName    = LOIPtr->Str;
    436           LanguageOk  = TRUE;
    437           break;
    438         }
    439       }
    440     }
    441 
    442     if (!LanguageOk) {
    443       continue;
    444     }
    445     //
    446     // Process each string for this language. We have to make 3 passes on the strings:
    447     //   Pass1: computes sizes and fill in the string pack header
    448     //   Pass2: write the array of offsets
    449     //   Pass3: write the strings
    450     //
    451     //
    452     // PASS 1: Fill in and print the HII string pack header
    453     //
    454     // Compute the size for this language package and write
    455     // the header out. Each string package contains:
    456     //   Header
    457     //   Offset[]  -- an array of offsets to strings, of type RELOFST each
    458     //   String[]  -- the actual strings themselves
    459     //
    460     fprintf (
    461       Fptr,
    462       "\n//******************************************************************************"
    463       "\n// Start of string definitions for %S/%S",
    464       Lang->LanguageName,
    465       Lang->PrintableLanguageName
    466       );
    467     memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER));
    468     StringPack.Header.Type        = EFI_HII_STRING;
    469     StringPack.NumStringPointers  = (UINT16) mDBData.NumStringIdentifiersReferenced;
    470     //
    471     // First string is the language name. If we're printing all languages, then
    472     // it's just the "spa". If we were given a list of languages to print, then it's
    473     // the "spacat" string. Compute its offset and fill in
    474     // the info in the header. Since we know the language name string's length,
    475     // and the printable language name follows it, use that info to fill in the
    476     // entry for the printable language name as well.
    477     //
    478     StringPack.LanguageNameString = (STRING_OFFSET) (sizeof (EFI_HII_STRING_PACK_HEADER) + (mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)));
    479     StringPack.PrintableLanguageName = (STRING_OFFSET) (StringPack.LanguageNameString + (wcslen (LangName) + 1) * sizeof (WCHAR));
    480     //
    481     // Add up the size of all strings so we can fill in our header.
    482     //
    483     Len = 0;
    484     for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
    485       //
    486       // For the first string (language name), we print out the "spacat" if they
    487       // requested it. We set LangName to point to the proper language name string above.
    488       //
    489       if (StringIndex == STRING_ID_LANGUAGE_NAME) {
    490         Len += (wcslen (LangName) + 1) * sizeof (WCHAR);
    491       } else {
    492         //
    493         // Find a string with this language.stringname
    494         //
    495         StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
    496         if (StringIdentifier == NULL) {
    497           Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
    498           return STATUS_ERROR;
    499         }
    500         //
    501         // Find a matching string if this string identifier was referenced
    502         //
    503         EmptyString.Flags = STRING_FLAGS_UNDEFINED;
    504         CurrString        = NULL;
    505         if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
    506           CurrString = StringDBFindString (
    507                         Lang->LanguageName,
    508                         StringIdentifier->StringName,
    509                         NULL,
    510                         LanguagesOfInterest,
    511                         IndirectionList
    512                         );
    513           if (NULL == CurrString) {
    514             //
    515             // If string for Lang->LanguageName is not found, try to get an English version
    516             //
    517             CurrString = StringDBFindString (
    518                           L"eng",
    519                           StringIdentifier->StringName,
    520                           NULL,
    521                           LanguagesOfInterest,
    522                           IndirectionList
    523                           );
    524           }
    525         }
    526 
    527         if (CurrString == NULL) {
    528           CurrString = &EmptyString;
    529           EmptyString.Flags |= StringIdentifier->Flags;
    530         }
    531 
    532         Len += CurrString->Size;
    533       }
    534     }
    535     StringPack.Header.Length =    sizeof (EFI_HII_STRING_PACK_HEADER)
    536                                 + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)
    537                                 + Len;
    538     //
    539     // Write out the header one byte at a time
    540     //
    541     Ptr = (UINT8 *) &StringPack;
    542     for (TempIndex = 0; TempIndex < sizeof (EFI_HII_STRING_PACK_HEADER); TempIndex++, Ptr++) {
    543       if ((TempIndex & 0x07) == 0) {
    544         fprintf (Fptr, "\n  ");
    545       }
    546 
    547       fprintf (Fptr, "0x%02X, ", (UINT32) *Ptr);
    548     }
    549 
    550     fprintf (Fptr, "\n  // offset 0x%X\n", sizeof (StringPack));
    551     //
    552     // PASS2 : write the offsets
    553     //
    554     // Traverse the list of strings again and write the array of offsets. The
    555     // offset to the first string is the size of the string pack header
    556     // plus the size of the offsets array. The other strings follow it.
    557     //
    558     StringIndex = 0;
    559     Offset      = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);
    560     for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
    561       //
    562       // Write the offset, followed by a useful comment
    563       //
    564       fprintf (Fptr, "  ");
    565       Ptr = (UINT8 *) &Offset;
    566       for (TempIndex = 0; TempIndex < sizeof (STRING_OFFSET); TempIndex++) {
    567         fprintf (Fptr, "0x%02X, ", (UINT32) Ptr[TempIndex]);
    568       }
    569       //
    570       // Find the string name
    571       //
    572       StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
    573       if (StringIdentifier == NULL) {
    574         Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
    575         return STATUS_ERROR;
    576       }
    577 
    578       fprintf (Fptr, " // offset to string %S (0x%04X)", StringIdentifier->StringName, StringIndex);
    579       //
    580       // For the first string (language name), we print out the "spacat" if they
    581       // requested it. We set LangName to point to the proper language name string above.
    582       //
    583       if (StringIndex == STRING_ID_LANGUAGE_NAME) {
    584         Offset += (wcslen (LangName) + 1) * sizeof (WCHAR);
    585         CurrString = StringDBFindString (
    586                       Lang->LanguageName,
    587                       StringIdentifier->StringName,
    588                       NULL, // scope
    589                       NULL,
    590                       NULL
    591                       );
    592       } else {
    593         //
    594         // Find a matching string
    595         //
    596         CurrString = StringDBFindString (
    597                       Lang->LanguageName,
    598                       StringIdentifier->StringName,
    599                       NULL,   // scope
    600                       LanguagesOfInterest,
    601                       IndirectionList
    602                       );
    603 
    604         if (NULL == CurrString) {
    605           CurrString = StringDBFindString (
    606                         L"eng",
    607                         StringIdentifier->StringName,
    608                         NULL, // scope
    609                         LanguagesOfInterest,
    610                         IndirectionList
    611                         );
    612         }
    613 
    614         EmptyString.LanguageName = Lang->LanguageName;
    615         if (CurrString == NULL) {
    616           CurrString        = &EmptyString;
    617           EmptyString.Flags = STRING_FLAGS_UNDEFINED;
    618         } else if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) {
    619           CurrString        = &EmptyString;
    620           EmptyString.Flags = 0;
    621         }
    622 
    623         Offset += CurrString->Size;
    624       }
    625       //
    626       // Print useful info about this string
    627       //
    628       if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) {
    629         fprintf (Fptr, " - not referenced");
    630       }
    631 
    632       if (CurrString->Flags & STRING_FLAGS_UNDEFINED) {
    633         fprintf (Fptr, " - not defined for this language");
    634       } else if (wcscmp (CurrString->LanguageName, Lang->LanguageName) != 0) {
    635         fprintf (
    636           Fptr,
    637           " - not defined for this language -- using secondary language %S definition",
    638           CurrString->LanguageName
    639           );
    640       }
    641 
    642       fprintf (Fptr, "\n");
    643     }
    644     //
    645     // For unreferenced string identifiers, print a message that they are not referenced anywhere
    646     //
    647     while (StringIndex < mDBData.NumStringIdentifiers) {
    648       StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
    649       if (StringIdentifier != NULL) {
    650         fprintf (Fptr, "  // %S not referenced\n", StringIdentifier->StringName);
    651       }
    652 
    653       StringIndex++;
    654     }
    655 
    656     //
    657     // PASS 3: write the strings themselves.
    658     // Keep track of how many bytes we write per line because some editors
    659     // (Visual Studio for instance) can't handle too long of lines.
    660     //
    661     Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);
    662     for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
    663       StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
    664       if (StringIdentifier == NULL) {
    665         Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
    666         return STATUS_ERROR;
    667       }
    668 
    669       fprintf (Fptr, "  // string %S offset 0x%08X\n  ", StringIdentifier->StringName, Offset);
    670       //
    671       // For the first string (language name), we print out the "spacat" if they
    672       // requested it. We set LangName to point to the proper language name string above.
    673       //
    674       if (StringIndex == STRING_ID_LANGUAGE_NAME) {
    675         TempStringPtr = LangName;
    676       } else {
    677         //
    678         // Find a matching string if this string identifier was referenced
    679         //
    680         CurrString = NULL;
    681         if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
    682           CurrString = StringDBFindString (
    683                         Lang->LanguageName,
    684                         StringIdentifier->StringName,
    685                         NULL,   // scope
    686                         LanguagesOfInterest,
    687                         IndirectionList
    688                         );
    689           if (NULL == CurrString) {
    690             CurrString = StringDBFindString (
    691                           L"eng",
    692                           StringIdentifier->StringName,
    693                           NULL, // scope
    694                           LanguagesOfInterest,
    695                           IndirectionList
    696                           );
    697           }
    698         }
    699 
    700         if (CurrString == NULL) {
    701           CurrString = &EmptyString;
    702         }
    703 
    704         TempStringPtr = CurrString->Str;
    705       }
    706 
    707       BytesThisLine = 0;
    708       for (TempIndex = 0; TempStringPtr[TempIndex] != 0; TempIndex++) {
    709         fprintf (
    710           Fptr,
    711           "0x%02X, 0x%02X, ",
    712           (UINT32) TempStringPtr[TempIndex] & 0xFF,
    713           (UINT32) ((TempStringPtr[TempIndex] >> 8) & 0xFF)
    714           );
    715         BytesThisLine += 2;
    716         Offset += 2;
    717         //
    718         // Let's say we only allow 14 per line
    719         //
    720         if (BytesThisLine > 14) {
    721           fprintf (Fptr, "\n  ");
    722           BytesThisLine = 0;
    723         }
    724       }
    725       //
    726       // Print NULL WCHAR at the end of this string.
    727       //
    728       fprintf (Fptr, "0x00, 0x00,\n");
    729       Offset += 2;
    730     }
    731     //
    732     // Sanity check the offset. Make sure our running offset is what we put in the
    733     // string pack header.
    734     //
    735     if (StringPack.Header.Length != Offset) {
    736       Error (
    737         __FILE__,
    738         __LINE__,
    739         0,
    740         "application error",
    741         "stringpack size 0x%X does not match final size 0x%X",
    742         StringPack.Header.Length,
    743         Offset
    744         );
    745     }
    746   }
    747   //
    748   // Print terminator string pack, closing brace and close the file.
    749   // The size of 0 triggers to the consumer that this is the end.
    750   //
    751   memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER));
    752   StringPack.Header.Type  = EFI_HII_STRING;
    753   Ptr                     = (UINT8 *) &StringPack;
    754   fprintf (Fptr, "\n  // strings terminator pack");
    755   for (TempIndex = 0; TempIndex < sizeof (StringPack); TempIndex++, Ptr++) {
    756     if ((TempIndex & 0x0F) == 0) {
    757       fprintf (Fptr, "\n  ");
    758     }
    759 
    760     fprintf (Fptr, "0x%02X, ", (UINT32) *Ptr);
    761   }
    762 
    763   fprintf (Fptr, "\n};\n");
    764   fclose (Fptr);
    765   return STATUS_SUCCESS;
    766 }
    767 
    768 /*****************************************************************************/
    769 
    770 /*++
    771 
    772 Routine Description:
    773 
    774   Dump the #define string names
    775 
    776 Arguments:
    777 
    778   FileName        - name of the output file to write
    779   BaseName        - used for the protection #ifndef/#endif
    780 
    781 Returns:
    782 
    783   STATUS
    784 
    785 --*/
    786 STATUS
    787 StringDBDumpStringDefines (
    788   INT8 *FileName,
    789   INT8 *BaseName
    790   )
    791 {
    792   FILE              *Fptr;
    793   STRING_IDENTIFIER *Identifier;
    794   INT8              CopyBaseName[100];
    795   UINT32            Index;
    796   const INT8        *StrDefHeader[] = {
    797     "#ifndef _%s_STRINGS_DEFINE_H_\n",
    798     "#define _%s_STRINGS_DEFINE_H_\n\n",
    799     NULL
    800   };
    801 
    802   if ((Fptr = fopen (FileName, "w")) == NULL) {
    803     Error (NULL, 0, 0, FileName, "failed to open output string defines file");
    804     return STATUS_ERROR;
    805   }
    806   //
    807   // Get the base source filename and convert to uppercase.
    808   //
    809   if (sizeof (CopyBaseName) <= strlen (BaseName) + 1) {
    810     Error (NULL, 0, 0, "application error", "StringDBDumpStringDefines() string length insufficient");
    811     return STATUS_ERROR;
    812   }
    813 
    814   strcpy (CopyBaseName, BaseName);
    815   for (Index = 0; CopyBaseName[Index] != 0; Index++) {
    816     if (islower (CopyBaseName[Index])) {
    817       CopyBaseName[Index] = (INT8) toupper (CopyBaseName[Index]);
    818     }
    819   }
    820   //
    821   // Assign index values to the string identifiers
    822   //
    823   StringDBAssignStringIndexes ();
    824   //
    825   // Write the standard header to the output file, and then the
    826   // protective #ifndef.
    827   //
    828   StringDBWriteStandardFileHeader (Fptr);
    829   for (Index = 0; StrDefHeader[Index] != NULL; Index++) {
    830     fprintf (Fptr, StrDefHeader[Index], CopyBaseName);
    831   }
    832   //
    833   // Print all the #defines for the string identifiers. Print identifiers
    834   // whose names start with '$' as comments. Add comments for string
    835   // identifiers not used as well.
    836   //
    837   Identifier = mDBData.StringIdentifier;
    838   while (Identifier != NULL) {
    839     if (Identifier->StringName[0] == L'$') {
    840       fprintf (Fptr, "// ");
    841     }
    842 
    843     if (Identifier->Flags & STRING_FLAGS_REFERENCED) {
    844       fprintf (Fptr, "#define %-40S 0x%04X\n", Identifier->StringName, Identifier->Index);
    845     } else {
    846       fprintf (Fptr, "//#define %-40S 0x%04X // not referenced\n", Identifier->StringName, Identifier->Index);
    847     }
    848 
    849     Identifier = Identifier->Next;
    850   }
    851 
    852   fprintf (Fptr, "\n#endif\n");
    853   fclose (Fptr);
    854   return STATUS_SUCCESS;
    855 }
    856 
    857 /*****************************************************************************/
    858 
    859 /*++
    860 
    861 Routine Description:
    862 
    863   Add a string identifier to the database.
    864 
    865 Arguments:
    866 
    867   StringName      - name of the string identifier. For example "STR_MY_STRING"
    868   NewId           - if an ID has been assigned
    869   Flags           - characteristics for the identifier
    870 
    871 Returns:
    872 
    873   STATUS
    874 
    875 --*/
    876 STATUS
    877 StringDBAddStringIdentifier (
    878   WCHAR     *StringName,
    879   UINT16    *NewId,
    880   UINT16    Flags
    881   )
    882 {
    883   STRING_IDENTIFIER *StringIdentifier;
    884   STATUS            Status;
    885   //
    886   // If it was already used for some other language, then we don't
    887   // need to add it. But set it to the current string identifier.
    888   // The referenced bit is sticky.
    889   //
    890   Status            = STATUS_SUCCESS;
    891   StringIdentifier  = StringDBFindStringIdentifierByName (StringName);
    892   if (StringIdentifier != NULL) {
    893     if (Flags & STRING_FLAGS_REFERENCED) {
    894       StringIdentifier->Flags |= STRING_FLAGS_REFERENCED;
    895     }
    896 
    897     mDBData.CurrentStringIdentifier = StringIdentifier;
    898     *NewId                          = (UINT16) StringIdentifier->Index;
    899     return Status;
    900   }
    901 
    902   StringIdentifier = (STRING_IDENTIFIER *) MALLOC (sizeof (STRING_IDENTIFIER));
    903   if (StringIdentifier == NULL) {
    904     Error (NULL, 0, 0, NULL, "memory allocation error");
    905     return STATUS_ERROR;
    906   }
    907 
    908   memset ((char *) StringIdentifier, 0, sizeof (STRING_IDENTIFIER));
    909   StringIdentifier->StringName = (WCHAR *) malloc ((wcslen (StringName) + 1) * sizeof (WCHAR));
    910   if (StringIdentifier->StringName == NULL) {
    911     Error (NULL, 0, 0, NULL, "memory allocation error");
    912     return STATUS_ERROR;
    913   }
    914 
    915   wcscpy (StringIdentifier->StringName, StringName);
    916   if (*NewId != STRING_ID_INVALID) {
    917     StringIdentifier->Index = *NewId;
    918     StringIdentifier->Flags |= STRING_FLAGS_INDEX_ASSIGNED;
    919     if (mDBData.NumStringIdentifiers <= StringIdentifier->Index) {
    920       mDBData.NumStringIdentifiers = StringIdentifier->Index + 1;
    921     }
    922   } else {
    923     StringIdentifier->Index = mDBData.NumStringIdentifiers++;
    924   }
    925 
    926   StringIdentifier->Flags |= Flags;
    927   //
    928   // Add it to our list of string identifiers
    929   //
    930   if (mDBData.StringIdentifier == NULL) {
    931     mDBData.StringIdentifier = StringIdentifier;
    932   } else {
    933     mDBData.LastStringIdentifier->Next = StringIdentifier;
    934   }
    935 
    936   mDBData.LastStringIdentifier    = StringIdentifier;
    937   mDBData.CurrentStringIdentifier = StringIdentifier;
    938   *NewId                          = (UINT16) StringIdentifier->Index;
    939   return Status;
    940 }
    941 
    942 /*****************************************************************************/
    943 
    944 /*++
    945 
    946 Routine Description:
    947 
    948   Add a new string to the database.
    949 
    950 Arguments:
    951 
    952   LanguageName    - "eng" or "spa" language name
    953   StringName      - "STR_MY_TEXT" string name
    954   Scope           - from the #scope statements in the string file
    955   Format          - if we should format the string
    956   Flags           - characteristic flags for the string
    957 
    958 Returns:
    959 
    960   STATUS
    961 
    962 Notes:
    963 
    964   Several of the fields can be "inherited" from the previous calls to
    965   our database functions. For example, if scope is NULL here, then
    966   we'll use the previous setting.
    967 
    968 --*/
    969 STATUS
    970 StringDBAddString (
    971   WCHAR   *LanguageName,
    972   WCHAR   *StringName,
    973   WCHAR   *Scope,
    974   WCHAR   *String,
    975   BOOLEAN Format,
    976   UINT16  Flags
    977   )
    978 {
    979   LANGUAGE_LIST     *Lang;
    980   UINT32            Size;
    981   STRING_LIST       *Str;
    982   UINT16            StringIndex;
    983   WCHAR             TempLangName[4];
    984   STRING_IDENTIFIER *StringIdentifier;
    985 
    986   //
    987   // Check that language name is exactly 3 characters, or emit an error.
    988   // Truncate at 3 if it's longer, or make it 3 if it's shorter.
    989   //
    990   if (LanguageName != NULL) {
    991     Size = wcslen (LanguageName);
    992     if (Size != 3) {
    993       ParserError (0, "invalid length for language name", "%S", LanguageName);
    994       if (Size > 3) {
    995         LanguageName[3] = 0;
    996       } else {
    997         //
    998         // Make a local copy of the language name string, and extend to
    999         // 3 characters since we make assumptions elsewhere in this program
   1000         // on the length.
   1001         //
   1002         wcscpy (TempLangName, LanguageName);
   1003         for (; Size < 3; Size++) {
   1004           TempLangName[Size] = L'?';
   1005         }
   1006 
   1007         TempLangName[3] = 0;
   1008         LanguageName    = TempLangName;
   1009       }
   1010     }
   1011   }
   1012   //
   1013   // If they specified a language, make sure they've defined it already
   1014   // via a #langdef statement. Otherwise use the current default language.
   1015   //
   1016   if (LanguageName != NULL) {
   1017     Lang = StringDBFindLanguageList (LanguageName);
   1018     if (Lang == NULL) {
   1019       ParserError (0, "language not defined", "%S", LanguageName);
   1020       return STATUS_ERROR;
   1021     } else {
   1022       StringDBSetCurrentLanguage (LanguageName);
   1023     }
   1024   } else {
   1025     Lang = mDBData.CurrentLanguage;
   1026     if (Lang == NULL) {
   1027       //
   1028       // Have to call SetLanguage() first
   1029       //
   1030       ParserError (0, "no language defined", "%S", StringName);
   1031       return STATUS_ERROR;
   1032     }
   1033   }
   1034   //
   1035   // If they didn't define a string identifier, use the last string identifier
   1036   // added.
   1037   //
   1038   if (StringName == NULL) {
   1039     StringName = mDBData.CurrentStringIdentifier->StringName;
   1040     if (StringName == NULL) {
   1041       ParserError (0, "no string identifier previously specified", NULL);
   1042       return STATUS_ERROR;
   1043     }
   1044   }
   1045   //
   1046   // If scope was not specified, use the default setting
   1047   //
   1048   if (Scope != NULL) {
   1049     Scope = DuplicateString (Scope);
   1050   } else {
   1051     Scope = DuplicateString (mDBData.CurrentScope);
   1052   }
   1053   //
   1054   // printf ("Adding string: %S.%S.%S\n", Lang->LanguageName, StringName, Scope);
   1055   //
   1056   // Check for duplicates for this Language.StringName.Scope. Allow multiple
   1057   // definitions of the language name and printable language name, since the
   1058   // user does not specifically define them.
   1059   //
   1060   if (StringDBFindString (Lang->LanguageName, StringName, Scope, NULL, NULL) != NULL) {
   1061     if ((wcscmp (StringName, LANGUAGE_NAME_STRING_NAME) == 0) &&
   1062         (wcscmp (StringName, PRINTABLE_LANGUAGE_NAME_STRING_NAME) == 0)
   1063         ) {
   1064       ParserError (
   1065         0,
   1066         "string multiply defined",
   1067         "Language.Name.Scope = %S.%S.%S",
   1068         Lang->LanguageName,
   1069         StringName,
   1070         Scope
   1071         );
   1072       return STATUS_ERROR;
   1073     }
   1074   }
   1075 
   1076   StringIndex = STRING_ID_INVALID;
   1077   if (StringDBAddStringIdentifier (StringName, &StringIndex, Flags) != STATUS_SUCCESS) {
   1078     return STATUS_ERROR;
   1079   }
   1080 
   1081   StringIdentifier = StringDBFindStringIdentifierByName (StringName);
   1082   //
   1083   // Add this string to the end of the strings for this language.
   1084   //
   1085   Str = (STRING_LIST *) malloc (sizeof (STRING_LIST));
   1086   if (Str == NULL) {
   1087     Error (NULL, 0, 0, NULL, "memory allocation error");
   1088     return STATUS_ERROR;
   1089   }
   1090 
   1091   memset ((char *) Str, 0, sizeof (STRING_LIST));
   1092   Size              = (wcslen (String) + 1) * sizeof (WCHAR);
   1093   Str->Flags        = Flags;
   1094   Str->Scope        = Scope;
   1095   Str->StringName   = StringIdentifier->StringName;
   1096   Str->LanguageName = DuplicateString (LanguageName);
   1097   Str->Str          = (WCHAR *) MALLOC (Size);
   1098   if (Str->Str == NULL) {
   1099     Error (NULL, 0, 0, NULL, "memory allocation error");
   1100     return STATUS_ERROR;
   1101   }
   1102   //
   1103   // If not formatting, just copy the string.
   1104   //
   1105   wcscpy (Str->Str, String);
   1106   if (Format) {
   1107     StringDBFormatString (Str->Str);
   1108   }
   1109   //
   1110   // Size may change after formatting. We set the size to
   1111   // the actual size of the string, including the null for
   1112   // easier processing later.
   1113   //
   1114   Str->Size = (wcslen (Str->Str) + 1) * sizeof (WCHAR);
   1115   if (Lang->String == NULL) {
   1116     Lang->String = Str;
   1117   } else {
   1118     Lang->LastString->Next = Str;
   1119   }
   1120 
   1121   Lang->LastString = Str;
   1122   return STATUS_SUCCESS;
   1123 }
   1124 
   1125 /*****************************************************************************/
   1126 
   1127 /*++
   1128 
   1129 Routine Description:
   1130 
   1131   Given a language name, see if a language list for it has been defined
   1132 
   1133 Arguments:
   1134 
   1135   LanguageName    - like "eng"
   1136 
   1137 Returns:
   1138 
   1139   A pointer to the language list
   1140 
   1141 --*/
   1142 static
   1143 LANGUAGE_LIST *
   1144 StringDBFindLanguageList (
   1145   WCHAR *LanguageName
   1146   )
   1147 {
   1148   LANGUAGE_LIST *Lang;
   1149 
   1150   Lang = mDBData.LanguageList;
   1151   while (Lang != NULL) {
   1152     if (wcscmp (LanguageName, Lang->LanguageName) == 0) {
   1153       break;
   1154     }
   1155 
   1156     Lang = Lang->Next;
   1157   }
   1158 
   1159   return Lang;
   1160 }
   1161 
   1162 /*****************************************************************************/
   1163 STATUS
   1164 StringDBSetCurrentLanguage (
   1165   WCHAR *LanguageName
   1166   )
   1167 {
   1168   LANGUAGE_LIST *Lang;
   1169 
   1170   Lang = StringDBFindLanguageList (LanguageName);
   1171   if (Lang == NULL) {
   1172     ParserError (0, "language not previously defined", "%S", LanguageName);
   1173     return STATUS_ERROR;
   1174   }
   1175 
   1176   mDBData.CurrentLanguage = Lang;
   1177   return STATUS_SUCCESS;
   1178 }
   1179 
   1180 /*****************************************************************************/
   1181 STATUS
   1182 StringDBAddLanguage (
   1183   WCHAR *LanguageName,
   1184   WCHAR *PrintableLanguageName
   1185   )
   1186 {
   1187   LANGUAGE_LIST *Lang;
   1188   //
   1189   // Check for redefinitions
   1190   //
   1191   Lang = StringDBFindLanguageList (LanguageName);
   1192   if (Lang != NULL) {
   1193     //
   1194     // Better be the same printable name
   1195     //
   1196     if (wcscmp (PrintableLanguageName, Lang->PrintableLanguageName) != 0) {
   1197       ParserError (
   1198         0,
   1199         "language redefinition",
   1200         "%S:%S != %S:%S",
   1201         Lang->LanguageName,
   1202         Lang->PrintableLanguageName,
   1203         LanguageName,
   1204         PrintableLanguageName
   1205         );
   1206       return STATUS_ERROR;
   1207       //
   1208       //    } else {
   1209       //      ParserWarning (0, "benign language redefinition", "%S", PrintableLanguageName);
   1210       //      return STATUS_WARNING;
   1211       //
   1212     }
   1213   } else {
   1214     //
   1215     // Allocate memory to keep track of this new language
   1216     //
   1217     Lang = (LANGUAGE_LIST *) malloc (sizeof (LANGUAGE_LIST));
   1218     if (Lang == NULL) {
   1219       Error (NULL, 0, 0, NULL, "memory allocation error");
   1220       return STATUS_ERROR;
   1221     }
   1222 
   1223     memset ((char *) Lang, 0, sizeof (LANGUAGE_LIST));
   1224     //
   1225     // Save the language name, then allocate memory to save the
   1226     // printable language name
   1227     //
   1228     Lang->LanguageName[3] = 0;
   1229     wcsncpy (Lang->LanguageName, LanguageName, 3);
   1230     Lang->PrintableLanguageName = (WCHAR *) malloc ((wcslen (PrintableLanguageName) + 1) * sizeof (WCHAR));
   1231     if (Lang->PrintableLanguageName == NULL) {
   1232       Error (NULL, 0, 0, NULL, "memory allocation error");
   1233       return STATUS_ERROR;
   1234     }
   1235 
   1236     wcscpy (Lang->PrintableLanguageName, PrintableLanguageName);
   1237 
   1238     if (mDBData.LanguageList == NULL) {
   1239       mDBData.LanguageList = Lang;
   1240     } else {
   1241       mDBData.LastLanguageList->Next = Lang;
   1242     }
   1243 
   1244     mDBData.LastLanguageList = Lang;
   1245   }
   1246   //
   1247   // Default is to make our active language this new one
   1248   //
   1249   StringDBSetCurrentLanguage (LanguageName);
   1250   //
   1251   // The first two strings for any language are the language name,
   1252   // followed by the printable language name. Add them and set them
   1253   // to referenced so they never get stripped out.
   1254   //
   1255   StringDBAddString (
   1256     LanguageName,
   1257     LANGUAGE_NAME_STRING_NAME,
   1258     NULL,
   1259     LanguageName,
   1260     FALSE,
   1261     STRING_FLAGS_REFERENCED
   1262     );
   1263   StringDBAddString (
   1264     LanguageName,
   1265     PRINTABLE_LANGUAGE_NAME_STRING_NAME,
   1266     NULL,
   1267     PrintableLanguageName,
   1268     FALSE,
   1269     STRING_FLAGS_REFERENCED
   1270     );
   1271   return STATUS_SUCCESS;
   1272 }
   1273 
   1274 /*****************************************************************************/
   1275 static
   1276 STRING_IDENTIFIER *
   1277 StringDBFindStringIdentifierByName (
   1278   WCHAR *StringName
   1279   )
   1280 {
   1281   STRING_IDENTIFIER *Identifier;
   1282 
   1283   Identifier = mDBData.StringIdentifier;
   1284   while (Identifier != NULL) {
   1285     if (wcscmp (StringName, Identifier->StringName) == 0) {
   1286       return Identifier;
   1287     }
   1288 
   1289     Identifier = Identifier->Next;
   1290   }
   1291 
   1292   return NULL;
   1293 }
   1294 
   1295 static
   1296 STRING_IDENTIFIER *
   1297 StringDBFindStringIdentifierByIndex (
   1298   UINT32    StringIndex
   1299   )
   1300 {
   1301   STRING_IDENTIFIER *Identifier;
   1302 
   1303   Identifier = mDBData.StringIdentifier;
   1304   while (Identifier != NULL) {
   1305     if (Identifier->Index == StringIndex) {
   1306       return Identifier;
   1307     }
   1308 
   1309     Identifier = Identifier->Next;
   1310   }
   1311 
   1312   return NULL;
   1313 }
   1314 
   1315 /*****************************************************************************/
   1316 static
   1317 void
   1318 StringDBWriteStandardFileHeader (
   1319   FILE *OutFptr
   1320   )
   1321 {
   1322   UINT32  TempIndex;
   1323   for (TempIndex = 0; mSourceFileHeader[TempIndex] != NULL; TempIndex++) {
   1324     fprintf (OutFptr, "%s\n", mSourceFileHeader[TempIndex]);
   1325   }
   1326 }
   1327 
   1328 /*****************************************************************************/
   1329 
   1330 /*++
   1331 
   1332 Routine Description:
   1333 
   1334   Given a Unicode string from an input file, reformat the string to replace
   1335   backslash control sequences with the appropriate encoding.
   1336 
   1337 Arguments:
   1338 
   1339   String        - pointer to string to reformat
   1340 
   1341 Returns:
   1342 
   1343   Nothing
   1344 
   1345 --*/
   1346 void
   1347 StringDBFormatString (
   1348   WCHAR   *String
   1349   )
   1350 {
   1351   WCHAR *From;
   1352   WCHAR *To;
   1353   int   HexNibbles;
   1354   WCHAR HexValue;
   1355   //
   1356   // Go through the string and process any formatting characters
   1357   //
   1358   From  = String;
   1359   To    = String;
   1360   while (*From) {
   1361     if (*From == UNICODE_BACKSLASH) {
   1362       //
   1363       // First look for \wide and replace with the appropriate control character. Note that
   1364       // when you have "define STR L"ABC"", then sizeof(ABC) is 8 because the null char is
   1365       // counted. Make adjustments for this. We advance From below, so subtract 2 each time.
   1366       //
   1367       if (wcsncmp (From, UNICODE_WIDE_STRING, sizeof (UNICODE_WIDE_STRING) / sizeof (WCHAR) - 1) == 0) {
   1368         *To = WIDE_CHAR;
   1369         From += sizeof (UNICODE_WIDE_STRING) / sizeof (WCHAR) - 2;
   1370       } else if (wcsncmp (From, UNICODE_NARROW_STRING, sizeof (UNICODE_NARROW_STRING) / sizeof (WCHAR) - 1) == 0) {
   1371         //
   1372         // Found: \narrow
   1373         //
   1374         *To = NARROW_CHAR;
   1375         From += sizeof (UNICODE_NARROW_STRING) / sizeof (WCHAR) - 2;
   1376       } else if (wcsncmp (From, UNICODE_NBR_STRING, sizeof (UNICODE_NBR_STRING) / sizeof (WCHAR) - 1) == 0) {
   1377         //
   1378         // Found: \nbr
   1379         //
   1380         *To = NON_BREAKING_CHAR;
   1381         From += sizeof (UNICODE_NBR_STRING) / sizeof (WCHAR) - 2;
   1382       } else if (wcsncmp (From, UNICODE_BR_STRING, sizeof (UNICODE_BR_STRING) / sizeof (WCHAR) - 1) == 0) {
   1383         //
   1384         // Found: \br -- pass through untouched
   1385         //
   1386         *To = *From;
   1387       } else {
   1388         //
   1389         // Standard one-character control sequences such as \n, \r, \\, or \x
   1390         //
   1391         From++;
   1392         switch (*From) {
   1393         case ASCII_TO_UNICODE ('n'):
   1394           *To = UNICODE_CR;
   1395           To++;
   1396           *To = UNICODE_LF;
   1397           break;
   1398 
   1399         //
   1400         // carriage return
   1401         //
   1402         case ASCII_TO_UNICODE ('r'):
   1403           *To = UNICODE_CR;
   1404           break;
   1405 
   1406         //
   1407         // backslash
   1408         //
   1409         case UNICODE_BACKSLASH:
   1410           *To = UNICODE_BACKSLASH;
   1411           break;
   1412 
   1413         //
   1414         // Tab
   1415         //
   1416         case ASCII_TO_UNICODE ('t'):
   1417           *To = UNICODE_TAB;
   1418           break;
   1419 
   1420         //
   1421         // embedded double-quote
   1422         //
   1423         case UNICODE_DOUBLE_QUOTE:
   1424           *To = UNICODE_DOUBLE_QUOTE;
   1425           break;
   1426 
   1427         //
   1428         // Hex Unicode character \x1234. We'll process up to 4 hex characters
   1429         //
   1430         case ASCII_TO_UNICODE ('x'):
   1431           HexValue = 0;
   1432           for (HexNibbles = 0; HexNibbles < 4; HexNibbles++) {
   1433             if ((From[1] >= UNICODE_0) && (From[1] <= UNICODE_9)) {
   1434               HexValue = (HexValue << 4) | (From[1] - UNICODE_0);
   1435             } else if ((From[1] >= UNICODE_a) && (From[1] <= UNICODE_f)) {
   1436               HexValue = (HexValue << 4) | (10 + From[1] - UNICODE_a);
   1437             } else if ((From[1] >= UNICODE_A) && (From[1] <= UNICODE_F)) {
   1438               HexValue = (HexValue << 4) | (10 + From[1] - UNICODE_A);
   1439             } else {
   1440               break;
   1441             }
   1442 
   1443             From++;
   1444           }
   1445 
   1446           if (HexNibbles == 0) {
   1447             ParserWarning (
   1448               0,
   1449               "expected at least one valid hex digit with \\x escaped character in string",
   1450               "\\%C",
   1451               *From
   1452               );
   1453           } else {
   1454             *To = HexValue;
   1455           }
   1456           break;
   1457 
   1458         default:
   1459           *To = UNICODE_SPACE;
   1460           ParserWarning (0, "invalid escaped character in string", "\\%C", *From);
   1461           break;
   1462         }
   1463       }
   1464     } else {
   1465       *To = *From;
   1466     }
   1467 
   1468     From++;
   1469     To++;
   1470   }
   1471 
   1472   *To = 0;
   1473 }
   1474 
   1475 /*****************************************************************************/
   1476 STATUS
   1477 StringDBReadDatabase (
   1478   INT8    *DBFileName,
   1479   BOOLEAN IgnoreIfNotExist,
   1480   BOOLEAN Verbose
   1481   )
   1482 {
   1483   STRING_DB_HEADER    DbHeader;
   1484   STATUS              Status;
   1485   FILE                *DBFptr;
   1486   DB_DATA_ITEM_HEADER DataItemHeader;
   1487 
   1488   Status  = STATUS_SUCCESS;
   1489   DBFptr  = NULL;
   1490   //
   1491   //  if (Verbose) {
   1492   //    fprintf (stdout, "Reading database file %s\n", DBFileName);
   1493   //  }
   1494   //
   1495   // Try to open the input file
   1496   //
   1497   if ((DBFptr = fopen (DBFileName, "rb")) == NULL) {
   1498     if (IgnoreIfNotExist) {
   1499       return STATUS_SUCCESS;
   1500     }
   1501 
   1502     Error (NULL, 0, 0, DBFileName, "failed to open input database file for reading");
   1503     return STATUS_ERROR;
   1504   }
   1505   //
   1506   // Read and verify the database header
   1507   //
   1508   if (fread ((void *) &DbHeader, sizeof (STRING_DB_HEADER), 1, DBFptr) != 1) {
   1509     Error (NULL, 0, 0, DBFileName, "failed to read header from database file");
   1510     Status = STATUS_ERROR;
   1511     goto Finish;
   1512   }
   1513 
   1514   if (DbHeader.Key != STRING_DB_KEY) {
   1515     Error (NULL, 0, 0, DBFileName, "invalid header in database file");
   1516     Status = STATUS_ERROR;
   1517     goto Finish;
   1518   }
   1519 
   1520   if ((DbHeader.Version & STRING_DB_MAJOR_VERSION_MASK) != (STRING_DB_VERSION & STRING_DB_MAJOR_VERSION_MASK)) {
   1521     Error (NULL, 0, 0, DBFileName, "incompatible database file version -- rebuild clean");
   1522     Status = STATUS_ERROR;
   1523     goto Finish;
   1524   }
   1525   //
   1526   // Read remaining items
   1527   //
   1528   while (fread (&DataItemHeader, sizeof (DataItemHeader), 1, DBFptr) == 1) {
   1529     switch (DataItemHeader.DataType) {
   1530     case DB_DATA_TYPE_STRING_IDENTIFIER:
   1531       StringDBReadStringIdentifier (DBFptr);
   1532       break;
   1533 
   1534     case DB_DATA_TYPE_LANGUAGE_DEFINITION:
   1535       StringDBReadLanguageDefinition (DBFptr);
   1536       break;
   1537 
   1538     case DB_DATA_TYPE_STRING_DEFINITION:
   1539       StringDBReadString (DBFptr);
   1540       break;
   1541 
   1542     default:
   1543       Error (
   1544         NULL,
   1545         0,
   1546         0,
   1547         "database corrupted",
   1548         "invalid data item type 0x%X at offset 0x%X",
   1549         (UINT32) DataItemHeader.DataType,
   1550         ftell (DBFptr) - sizeof (DataItemHeader)
   1551         );
   1552       Status = STATUS_ERROR;
   1553       goto Finish;
   1554     }
   1555   }
   1556 
   1557 Finish:
   1558   if (DBFptr != NULL) {
   1559     fclose (DBFptr);
   1560   }
   1561 
   1562   return Status;
   1563 }
   1564 
   1565 /*****************************************************************************/
   1566 
   1567 /*++
   1568 
   1569 Routine Description:
   1570 
   1571   Write everything we know to the output database file. Write:
   1572 
   1573   Database header
   1574   String identifiers[]
   1575   StringPacks[]
   1576 
   1577 Arguments:
   1578 
   1579   DBFileName    - name of the file to write to
   1580   Verbose       - for debug purposes, print info messages along the way.
   1581 
   1582 Returns:
   1583 
   1584   STATUS
   1585 
   1586 --*/
   1587 STATUS
   1588 StringDBWriteDatabase (
   1589   INT8    *DBFileName,
   1590   BOOLEAN Verbose
   1591   )
   1592 {
   1593   STRING_DB_HEADER  DbHeader;
   1594   UINT32            Counter;
   1595   UINT32            StrLen;
   1596   LANGUAGE_LIST     *Lang;
   1597   STRING_IDENTIFIER *StringIdentifier;
   1598   STRING_LIST       *StrList;
   1599   FILE              *DBFptr;
   1600 
   1601   if (Verbose) {
   1602     fprintf (stdout, "Writing database %s\n", DBFileName);
   1603   }
   1604 
   1605   if ((DBFptr = fopen (DBFileName, "wb")) == NULL) {
   1606     Error (NULL, 0, 0, DBFileName, "failed to open output database file for writing");
   1607     return STATUS_ERROR;
   1608   }
   1609   //
   1610   // Fill in and write the database header
   1611   //
   1612   memset (&DbHeader, 0, sizeof (STRING_DB_HEADER));
   1613   DbHeader.HeaderSize = sizeof (STRING_DB_HEADER);
   1614   DbHeader.Key        = STRING_DB_KEY;
   1615   DbHeader.Version    = STRING_DB_VERSION;
   1616   //
   1617   // Count the number of languages we have
   1618   //
   1619   for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
   1620     DbHeader.NumLanguages++;
   1621   }
   1622   //
   1623   // Count up how many string identifiers we have, and total up the
   1624   // size of the names plus the size of the flags field we will
   1625   // write out too.
   1626   //
   1627   DbHeader.NumStringIdenfiers = mDBData.NumStringIdentifiers;
   1628   StringIdentifier            = mDBData.StringIdentifier;
   1629   for (Counter = 0; Counter < mDBData.NumStringIdentifiers; Counter++) {
   1630     StrLen = wcslen (StringIdentifier->StringName) + 1;
   1631     DbHeader.StringIdentifiersSize += StrLen * sizeof (WCHAR) + sizeof (StringIdentifier->Flags);
   1632     StringIdentifier = StringIdentifier->Next;
   1633   }
   1634 
   1635   //
   1636   // Write the header
   1637   //
   1638   fwrite (&DbHeader, sizeof (STRING_DB_HEADER), 1, DBFptr);
   1639   if (Verbose) {
   1640     fprintf (stdout, "  Number of string identifiers  0x%04X\n", DbHeader.NumStringIdenfiers);
   1641     fprintf (stdout, "  Number of languages           %d\n", DbHeader.NumLanguages);
   1642   }
   1643   //
   1644   // Write the string identifiers
   1645   //
   1646   for (StringIdentifier = mDBData.StringIdentifier; StringIdentifier != NULL; StringIdentifier = StringIdentifier->Next) {
   1647     StringDBWriteStringIdentifier (
   1648       DBFptr,
   1649       (UINT16) StringIdentifier->Index,
   1650       StringIdentifier->Flags,
   1651       StringIdentifier->StringName
   1652       );
   1653   }
   1654   //
   1655   // Now write all the strings for each language
   1656   //
   1657   for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
   1658     StringDBWriteLanguageDefinition (DBFptr, Lang->LanguageName, Lang->PrintableLanguageName);
   1659     for (StrList = Lang->String; StrList != NULL; StrList = StrList->Next) {
   1660       StringDBWriteString (
   1661         DBFptr,
   1662         StrList->Flags,
   1663         Lang->LanguageName,
   1664         StrList->StringName,
   1665         StrList->Scope,
   1666         StrList->Str
   1667         );
   1668     }
   1669   }
   1670 
   1671   fclose (DBFptr);
   1672   return STATUS_SUCCESS;
   1673 }
   1674 
   1675 STATUS
   1676 StringDBSetStringReferenced (
   1677   INT8      *StringIdentifierName,
   1678   BOOLEAN   IgnoreNotFound
   1679   )
   1680 {
   1681   STRING_IDENTIFIER *Id;
   1682   WCHAR             *WName;
   1683   STATUS            Status;
   1684   //
   1685   // See if it's already been defined.
   1686   //
   1687   Status  = STATUS_SUCCESS;
   1688   WName   = (WCHAR *) malloc ((strlen (StringIdentifierName) + 1) * sizeof (WCHAR));
   1689 #ifdef USE_VC8
   1690   swprintf (WName, (strlen (StringIdentifierName) + 1) * sizeof (WCHAR), L"%S", StringIdentifierName);
   1691 #else
   1692   swprintf (WName, L"%S", StringIdentifierName);
   1693 #endif
   1694   Id = StringDBFindStringIdentifierByName (WName);
   1695   if (Id != NULL) {
   1696     Id->Flags |= STRING_FLAGS_REFERENCED;
   1697   } else {
   1698     if (IgnoreNotFound == 0) {
   1699       ParserWarning (0, StringIdentifierName, "string identifier not found in database");
   1700       Status = STATUS_WARNING;
   1701     }
   1702   }
   1703 
   1704   free (WName);
   1705   return Status;
   1706 }
   1707 
   1708 /*****************************************************************************/
   1709 
   1710 /*++
   1711 
   1712 Routine Description:
   1713 
   1714   Dump the contents of a database to an output unicode file.
   1715 
   1716 Arguments:
   1717 
   1718   DBFileName        - name of the pre-existing database file to read
   1719   OutputFileName    - name of the file to dump the database contents to
   1720   Verbose           - for printing of additional info useful for debugging
   1721 
   1722 Returns:
   1723 
   1724   STATUS
   1725 
   1726 Notes:
   1727 
   1728   There's some issue with the unicode printing routines. Therefore to
   1729   write to the output file properly, open it as binary and use fwrite.
   1730   Ideally we could open it with just L"w" and use fwprintf().
   1731 
   1732 --*/
   1733 STATUS
   1734 StringDBDumpDatabase (
   1735   INT8                *DBFileName,
   1736   INT8                *OutputFileName,
   1737   BOOLEAN             Verbose
   1738   )
   1739 {
   1740   LANGUAGE_LIST     *Lang;
   1741   STRING_IDENTIFIER *StringIdentifier;
   1742   STRING_LIST       *StrList;
   1743   FILE              *OutFptr;
   1744   WCHAR             WChar;
   1745   WCHAR             *WOutputFileName;
   1746   WCHAR             CrLf[2];
   1747   WCHAR             Line[200];
   1748   WCHAR             *Scope;
   1749   //
   1750   // This function assumes the database has already been read, and
   1751   // we're just dumping our internal data structures to a unicode file.
   1752   //
   1753   if (Verbose) {
   1754     fprintf (stdout, "Dumping database file %s\n", DBFileName);
   1755   }
   1756 
   1757   WOutputFileName = AsciiToWchar (OutputFileName);
   1758   OutFptr         = _wfopen (WOutputFileName, L"wb");
   1759   free (WOutputFileName);
   1760   if (OutFptr == NULL) {
   1761     Error (NULL, 0, 0, OutputFileName, "failed to open output file for writing");
   1762     return STATUS_ERROR;
   1763   }
   1764 
   1765   WChar = UNICODE_FILE_START;
   1766   fwrite (&WChar, sizeof (WCHAR), 1, OutFptr);
   1767   CrLf[1] = UNICODE_LF;
   1768   CrLf[0] = UNICODE_CR;
   1769   //
   1770   // The default control character is '/'. Make it '#' by writing
   1771   // "/=#" to the output file.
   1772   //
   1773 #ifdef USE_VC8
   1774   swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"/=#");
   1775 #else
   1776   swprintf (Line, L"/=#");
   1777 #endif
   1778   fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
   1779   fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
   1780   fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
   1781   //
   1782   // Dump all the string identifiers and their values
   1783   //
   1784   StringDBAssignStringIndexes ();
   1785   for (StringIdentifier = mDBData.StringIdentifier; StringIdentifier != NULL; StringIdentifier = StringIdentifier->Next) {
   1786     //
   1787     // Write the "#define " string
   1788     //
   1789     if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
   1790 #ifdef USE_VC8
   1791       swprintf (
   1792         Line,
   1793         wcslen(Line) * sizeof (WCHAR),
   1794         L"%s %-60.60s 0x%04X",
   1795         DEFINE_STR,
   1796         StringIdentifier->StringName,
   1797         StringIdentifier->Index
   1798         );
   1799 #else
   1800       swprintf (
   1801         Line,
   1802         L"%s %-60.60s 0x%04X",
   1803         DEFINE_STR,
   1804         StringIdentifier->StringName,
   1805         StringIdentifier->Index
   1806         );
   1807 #endif
   1808     } else {
   1809 #ifdef USE_VC8
   1810       swprintf (
   1811         Line,
   1812         wcslen(Line) * sizeof (WCHAR),
   1813         L"%s %-60.60s 0x%04X  // NOT REFERENCED",
   1814         DEFINE_STR,
   1815         StringIdentifier->StringName,
   1816         StringIdentifier->Index
   1817         );
   1818 #else
   1819       swprintf (
   1820         Line,
   1821         L"%s %-60.60s 0x%04X  // NOT REFERENCED",
   1822         DEFINE_STR,
   1823         StringIdentifier->StringName,
   1824         StringIdentifier->Index
   1825         );
   1826 #endif
   1827     }
   1828 
   1829     fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
   1830     fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
   1831   }
   1832 
   1833   fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
   1834   //
   1835   // Now write all the strings for each language.
   1836   //
   1837   WChar = UNICODE_DOUBLE_QUOTE;
   1838   Scope = NULL;
   1839   for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
   1840     fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
   1841 #ifdef USE_VC8
   1842     swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"#langdef %s \"%s\"", Lang->LanguageName, Lang->PrintableLanguageName);
   1843 #else
   1844     swprintf (Line, L"#langdef %s \"%s\"", Lang->LanguageName, Lang->PrintableLanguageName);
   1845 #endif
   1846     fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
   1847     fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
   1848     fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
   1849     //
   1850     // Now the strings (in double-quotes) for this language. Write
   1851     // #string STR_NAME  #language eng "string"
   1852     //
   1853     for (StrList = Lang->String; StrList != NULL; StrList = StrList->Next) {
   1854       //
   1855       // Print the internal flags for debug
   1856       //
   1857 #ifdef USE_VC8
   1858       swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"// flags=0x%02X", (UINT32) StrList->Flags);
   1859 #else
   1860       swprintf (Line, L"// flags=0x%02X", (UINT32) StrList->Flags);
   1861 #endif
   1862       fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
   1863       fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
   1864       //
   1865       // Print the scope if changed
   1866       //
   1867       if ((Scope == NULL) || (wcscmp (Scope, StrList->Scope) != 0)) {
   1868 #ifdef USE_VC8
   1869         swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"#scope %s", StrList->Scope);
   1870 #else
   1871         swprintf (Line, L"#scope %s", StrList->Scope);
   1872 #endif
   1873         fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
   1874         fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
   1875         Scope = StrList->Scope;
   1876       }
   1877 
   1878 #ifdef USE_VC8
   1879       swprintf (
   1880         Line,
   1881         wcslen(Line) * sizeof (WCHAR),
   1882         L"#string %-50.50s #language %s \"",
   1883         StrList->StringName,
   1884         Lang->LanguageName
   1885         );
   1886 #else
   1887       swprintf (
   1888         Line,
   1889         L"#string %-50.50s #language %s \"",
   1890         StrList->StringName,
   1891         Lang->LanguageName
   1892         );
   1893 #endif
   1894       fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
   1895       fwrite (StrList->Str, StrList->Size - sizeof (WCHAR), 1, OutFptr);
   1896 #ifdef USE_VC8
   1897       swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"\"");
   1898 #else
   1899       swprintf (Line, L"\"");
   1900 #endif
   1901       fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);
   1902       fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);
   1903     }
   1904   }
   1905 
   1906   fclose (OutFptr);
   1907   return STATUS_SUCCESS;
   1908 }
   1909 
   1910 /*****************************************************************************/
   1911 
   1912 /*++
   1913 
   1914 Routine Description:
   1915 
   1916   Given a primary language, a string identifier number, and a list of
   1917   languages, find a secondary string.
   1918 
   1919 Arguments:
   1920 
   1921   LanguageName      - primary language, like "spa"
   1922   StringId          - string index value
   1923   LanguageList      - linked list of "eng", "spa+cat",...
   1924 
   1925 Returns:
   1926 
   1927   Pointer to a secondary string if found. NULL otherwise.
   1928 
   1929 Notes:
   1930 
   1931   Given: LanguageName "spa"   and  LanguageList "spa+cat", match the
   1932   "spa" and extract the "cat" and see if there is a string defined
   1933   for "cat".StringId.
   1934 
   1935 --*/
   1936 static
   1937 STATUS
   1938 StringDBWriteStringIdentifier (
   1939   FILE                *DBFptr,
   1940   UINT16              StringId,
   1941   UINT16              Flags,
   1942   WCHAR               *IdentifierName
   1943   )
   1944 {
   1945   DB_DATA_ITEM_HEADER Hdr;
   1946   memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER));
   1947   Hdr.DataType = DB_DATA_TYPE_STRING_IDENTIFIER;
   1948   if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) {
   1949     Error (NULL, 0, 0, "failed to write string to output database file", NULL);
   1950     return STATUS_ERROR;
   1951   }
   1952 
   1953   if (fwrite (&StringId, sizeof (StringId), 1, DBFptr) != 1) {
   1954     Error (NULL, 0, 0, "failed to write StringId to output database", NULL);
   1955     return STATUS_ERROR;
   1956   }
   1957 
   1958   if (fwrite (&Flags, sizeof (Flags), 1, DBFptr) != 1) {
   1959     Error (NULL, 0, 0, "failed to write StringId flags to output database", NULL);
   1960     return STATUS_ERROR;
   1961   }
   1962 
   1963   if (StringDBWriteGenericString (DBFptr, IdentifierName) != STATUS_SUCCESS) {
   1964     return STATUS_ERROR;
   1965   }
   1966 
   1967   return STATUS_SUCCESS;
   1968 }
   1969 
   1970 static
   1971 STATUS
   1972 StringDBReadStringIdentifier (
   1973   FILE                *DBFptr
   1974   )
   1975 {
   1976   WCHAR   *IdentifierName;
   1977   UINT16  Flags;
   1978   UINT16  StringId;
   1979   UINT16  Size;
   1980 
   1981   if (fread (&StringId, sizeof (StringId), 1, DBFptr) != 1) {
   1982     Error (NULL, 0, 0, "failed to read StringId from database", NULL);
   1983     return STATUS_ERROR;
   1984   }
   1985 
   1986   if (fread (&Flags, sizeof (Flags), 1, DBFptr) != 1) {
   1987     Error (NULL, 0, 0, "failed to read StringId flags from database", NULL);
   1988     return STATUS_ERROR;
   1989   }
   1990 
   1991   if (StringDBReadGenericString (DBFptr, &Size, &IdentifierName) != STATUS_SUCCESS) {
   1992     return STATUS_ERROR;
   1993   }
   1994 
   1995   StringDBAddStringIdentifier (IdentifierName, &StringId, Flags);
   1996   //
   1997   // printf ("STRID:  0x%04X %S\n", (UINT32)StringId, IdentifierName);
   1998   //
   1999   FREE (IdentifierName);
   2000   return STATUS_SUCCESS;
   2001 }
   2002 
   2003 static
   2004 STATUS
   2005 StringDBWriteString (
   2006   FILE            *DBFptr,
   2007   UINT16          Flags,
   2008   WCHAR           *Language,
   2009   WCHAR           *StringName,
   2010   WCHAR           *Scope,
   2011   WCHAR           *Str
   2012   )
   2013 {
   2014   DB_DATA_ITEM_HEADER Hdr;
   2015   memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER));
   2016   Hdr.DataType = DB_DATA_TYPE_STRING_DEFINITION;
   2017   if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) {
   2018     Error (NULL, 0, 0, "failed to write string header to output database file", NULL);
   2019     return STATUS_ERROR;
   2020   }
   2021 
   2022   if (fwrite (&Flags, sizeof (Flags), 1, DBFptr) != 1) {
   2023     Error (NULL, 0, 0, "failed to write string flags to output database", NULL);
   2024     return STATUS_ERROR;
   2025   }
   2026 
   2027   if (StringDBWriteGenericString (DBFptr, Language) != STATUS_SUCCESS) {
   2028     return STATUS_ERROR;
   2029   }
   2030 
   2031   if (StringDBWriteGenericString (DBFptr, StringName) != STATUS_SUCCESS) {
   2032     return STATUS_ERROR;
   2033   }
   2034 
   2035   if (StringDBWriteGenericString (DBFptr, Scope) != STATUS_SUCCESS) {
   2036     return STATUS_ERROR;
   2037   }
   2038 
   2039   if (StringDBWriteGenericString (DBFptr, Str) != STATUS_SUCCESS) {
   2040     return STATUS_ERROR;
   2041   }
   2042   //
   2043   // printf ("DBWriteString: %S.%S.%S\n", Language, StringName, Scope);
   2044   //
   2045   return STATUS_SUCCESS;
   2046 }
   2047 
   2048 static
   2049 STATUS
   2050 StringDBReadString (
   2051   FILE            *DBFptr
   2052   )
   2053 {
   2054   UINT16  Flags;
   2055   UINT16  Size;
   2056   WCHAR   *Language;
   2057   WCHAR   *StringName;
   2058   WCHAR   *Scope;
   2059   WCHAR   *Str;
   2060 
   2061   if (fread (&Flags, sizeof (Flags), 1, DBFptr) != 1) {
   2062     Error (NULL, 0, 0, "failed to read string flags from database", NULL);
   2063     return STATUS_ERROR;
   2064   }
   2065 
   2066   if (StringDBReadGenericString (DBFptr, &Size, &Language) != STATUS_SUCCESS) {
   2067     return STATUS_ERROR;
   2068   }
   2069 
   2070   if (StringDBReadGenericString (DBFptr, &Size, &StringName) != STATUS_SUCCESS) {
   2071     return STATUS_ERROR;
   2072   }
   2073 
   2074   if (StringDBReadGenericString (DBFptr, &Size, &Scope) != STATUS_SUCCESS) {
   2075     return STATUS_ERROR;
   2076   }
   2077 
   2078   if (StringDBReadGenericString (DBFptr, &Size, &Str) != STATUS_SUCCESS) {
   2079     return STATUS_ERROR;
   2080   }
   2081   //
   2082   // If the first or second string (language name and printable language name),
   2083   // then skip them. They're added via language definitions data items in
   2084   // the database.
   2085   //
   2086   if (StringName[0] != L'$') {
   2087     StringDBAddString (Language, StringName, Scope, Str, FALSE, Flags);
   2088   }
   2089   //
   2090   // printf ("DBReadString: %S.%S.%S\n", Language, StringName, Scope);
   2091   //
   2092   FREE (Language);
   2093   FREE (StringName);
   2094   if (Str != NULL) {
   2095     FREE (Str);
   2096   }
   2097 
   2098   if (Scope != NULL) {
   2099     FREE (Scope);
   2100   }
   2101 
   2102   return STATUS_SUCCESS;
   2103 }
   2104 
   2105 static
   2106 STATUS
   2107 StringDBWriteLanguageDefinition (
   2108   FILE            *DBFptr,
   2109   WCHAR           *LanguageName,
   2110   WCHAR           *PrintableLanguageName
   2111   )
   2112 {
   2113   DB_DATA_ITEM_HEADER Hdr;
   2114   memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER));
   2115   Hdr.DataType = DB_DATA_TYPE_LANGUAGE_DEFINITION;
   2116   if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) {
   2117     Error (NULL, 0, 0, "failed to write string to output database file", NULL);
   2118     return STATUS_ERROR;
   2119   }
   2120 
   2121   if (StringDBWriteGenericString (DBFptr, LanguageName) != STATUS_SUCCESS) {
   2122     return STATUS_ERROR;
   2123   }
   2124 
   2125   if (StringDBWriteGenericString (DBFptr, PrintableLanguageName) != STATUS_SUCCESS) {
   2126     return STATUS_ERROR;
   2127   }
   2128 
   2129   return STATUS_SUCCESS;
   2130 }
   2131 
   2132 static
   2133 STATUS
   2134 StringDBReadLanguageDefinition (
   2135   FILE            *DBFptr
   2136   )
   2137 {
   2138   WCHAR   *LanguageName;
   2139   WCHAR   *PrintableLanguageName;
   2140   UINT16  Size;
   2141   STATUS  Status;
   2142 
   2143   if (StringDBReadGenericString (DBFptr, &Size, &LanguageName) != STATUS_SUCCESS) {
   2144     return STATUS_ERROR;
   2145   }
   2146 
   2147   if (StringDBReadGenericString (DBFptr, &Size, &PrintableLanguageName) != STATUS_SUCCESS) {
   2148     return STATUS_ERROR;
   2149   }
   2150   //
   2151   // printf("LANG: %S %S\n", LanguageName, PrintableLanguageName);
   2152   //
   2153   Status = StringDBAddLanguage (LanguageName, PrintableLanguageName);
   2154   FREE (LanguageName);
   2155   FREE (PrintableLanguageName);
   2156   return Status;
   2157 }
   2158 //
   2159 // All unicode strings in the database consist of a UINT16 length
   2160 // field, followed by the string itself. This routine reads one
   2161 // of those and returns the info.
   2162 //
   2163 static
   2164 STATUS
   2165 StringDBReadGenericString (
   2166   FILE      *DBFptr,
   2167   UINT16    *Size,
   2168   WCHAR     **Str
   2169   )
   2170 {
   2171   UINT16  LSize;
   2172   UINT16  Flags;
   2173   WCHAR   *LStr;
   2174 
   2175   if (fread (&LSize, sizeof (UINT16), 1, DBFptr) != 1) {
   2176     Error (NULL, 0, 0, "failed to read a string length field from the database", NULL);
   2177     return STATUS_ERROR;
   2178   }
   2179 
   2180   if (fread (&Flags, sizeof (UINT16), 1, DBFptr) != 1) {
   2181     Error (NULL, 0, 0, "failed to read a string flags field from the database", NULL);
   2182     return STATUS_ERROR;
   2183   }
   2184 
   2185   LStr = MALLOC (LSize);
   2186   if (LStr == NULL) {
   2187     Error (__FILE__, __LINE__, 0, "memory allocation failed reading the database", NULL);
   2188     return STATUS_ERROR;
   2189   }
   2190 
   2191   if (fread (LStr, sizeof (WCHAR), (UINT32) LSize / sizeof (WCHAR), DBFptr) != (UINT32) LSize / sizeof (WCHAR)) {
   2192     Error (NULL, 0, 0, "failed to read string from database", NULL);
   2193     Error (NULL, 0, 0, "database read failure", "offset 0x%X", ftell (DBFptr));
   2194     free (LStr);
   2195     return STATUS_ERROR;
   2196   }
   2197   //
   2198   // printf ("DBR: %S\n", LStr);
   2199   //
   2200   // If the flags field indicated we were asked to write a NULL string, then
   2201   // return them a NULL pointer.
   2202   //
   2203   if (Flags & STRING_FLAGS_UNDEFINED) {
   2204     *Size = 0;
   2205     *Str  = NULL;
   2206   } else {
   2207     *Size = LSize;
   2208     *Str  = LStr;
   2209   }
   2210 
   2211   return STATUS_SUCCESS;
   2212 }
   2213 
   2214 static
   2215 STATUS
   2216 StringDBWriteGenericString (
   2217   FILE      *DBFptr,
   2218   WCHAR     *Str
   2219   )
   2220 {
   2221   UINT16  Size;
   2222   UINT16  Flags;
   2223   WCHAR   ZeroString[1];
   2224   //
   2225   // Strings in the database consist of a size UINT16 followed
   2226   // by the string itself.
   2227   //
   2228   if (Str == NULL) {
   2229     ZeroString[0] = 0;
   2230     Str           = ZeroString;
   2231     Size          = sizeof (ZeroString);
   2232     Flags         = STRING_FLAGS_UNDEFINED;
   2233   } else {
   2234     Flags = 0;
   2235     Size  = (UINT16) ((wcslen (Str) + 1) * sizeof (WCHAR));
   2236   }
   2237 
   2238   if (fwrite (&Size, sizeof (UINT16), 1, DBFptr) != 1) {
   2239     Error (NULL, 0, 0, "failed to write string size to database", NULL);
   2240     return STATUS_ERROR;
   2241   }
   2242 
   2243   if (fwrite (&Flags, sizeof (UINT16), 1, DBFptr) != 1) {
   2244     Error (NULL, 0, 0, "failed to write string flags to database", NULL);
   2245     return STATUS_ERROR;
   2246   }
   2247 
   2248   if (fwrite (Str, sizeof (WCHAR), Size / sizeof (WCHAR), DBFptr) != Size / sizeof (WCHAR)) {
   2249     Error (NULL, 0, 0, "failed to write string to database", NULL);
   2250     return STATUS_ERROR;
   2251   }
   2252 
   2253   return STATUS_SUCCESS;
   2254 }
   2255 
   2256 static
   2257 STRING_LIST *
   2258 StringDBFindString (
   2259   WCHAR                       *LanguageName,
   2260   WCHAR                       *StringName,
   2261   WCHAR                       *Scope,
   2262   WCHAR_STRING_LIST           *LanguagesOfInterest,
   2263   WCHAR_MATCHING_STRING_LIST  *IndirectionList
   2264   )
   2265 {
   2266   LANGUAGE_LIST               *Lang;
   2267   STRING_LIST                 *CurrString;
   2268   WCHAR_MATCHING_STRING_LIST  *IndListPtr;
   2269   WCHAR                       TempLangName[LANGUAGE_IDENTIFIER_NAME_LEN + 1];
   2270   WCHAR                       *WCharPtr;
   2271 
   2272   //
   2273   // If we were given an indirection list, then see if one was specified for this
   2274   // string identifier. That is to say, if the indirection says "STR_ID_MY_FAVORITE MyScope",
   2275   // then if this string name matches one in the list, then do a lookup with the
   2276   // specified scope and return that value.
   2277   //
   2278   if (IndirectionList != NULL) {
   2279     for (IndListPtr = IndirectionList; IndListPtr != NULL; IndListPtr = IndListPtr->Next) {
   2280       if (wcscmp (StringName, IndListPtr->Str1) == 0) {
   2281         CurrString = StringDBFindString (LanguageName, StringName, IndListPtr->Str2, LanguagesOfInterest, NULL);
   2282         if (CurrString != NULL) {
   2283           return CurrString;
   2284         }
   2285       }
   2286     }
   2287   }
   2288   //
   2289   // First look for exact match language.stringname
   2290   //
   2291   for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
   2292     if (wcscmp (LanguageName, Lang->LanguageName) == 0) {
   2293       //
   2294       // Found language match. Try to find string name match
   2295       //
   2296       for (CurrString = Lang->String; CurrString != NULL; CurrString = CurrString->Next) {
   2297         if (wcscmp (StringName, CurrString->StringName) == 0) {
   2298           //
   2299           // Found a string name match. See if we're supposed to find
   2300           // a scope match.
   2301           //
   2302           if (Scope != NULL) {
   2303             if (wcscmp (CurrString->Scope, Scope) == 0) {
   2304               return CurrString;
   2305             }
   2306           } else {
   2307             return CurrString;
   2308           }
   2309         }
   2310       }
   2311     }
   2312   }
   2313   //
   2314   // If we got here, then we didn't find a match. Look for secondary string
   2315   // matches. That is to say, if we're processing "spa", and they requested
   2316   // "spa+cat", then recursively call with "cat"
   2317   //
   2318   while (LanguagesOfInterest != NULL) {
   2319     //
   2320     // If this is the language we're looking for, then process the
   2321     // languages of interest list for it.
   2322     //
   2323     if (wcsncmp (LanguageName, LanguagesOfInterest->Str, LANGUAGE_IDENTIFIER_NAME_LEN) == 0) {
   2324       WCharPtr = LanguagesOfInterest->Str + LANGUAGE_IDENTIFIER_NAME_LEN;
   2325       while (*WCharPtr) {
   2326         //
   2327         // Double-check the length, though it should have been checked on the
   2328         // command line.
   2329         //
   2330         if (wcslen (WCharPtr) < LANGUAGE_IDENTIFIER_NAME_LEN) {
   2331           Error (NULL, 0, 0, "malformed alternate language list", "%S", LanguagesOfInterest->Str);
   2332           return NULL;
   2333         }
   2334 
   2335         wcsncpy (TempLangName, WCharPtr, LANGUAGE_IDENTIFIER_NAME_LEN);
   2336         TempLangName[LANGUAGE_IDENTIFIER_NAME_LEN]  = 0;
   2337         CurrString = StringDBFindString (TempLangName, StringName, NULL, NULL, IndirectionList);
   2338         if (CurrString != NULL) {
   2339           return CurrString;
   2340         }
   2341 
   2342         WCharPtr += LANGUAGE_IDENTIFIER_NAME_LEN;
   2343       }
   2344     }
   2345 
   2346     LanguagesOfInterest = LanguagesOfInterest->Next;
   2347   }
   2348 
   2349   return NULL;
   2350 }
   2351 
   2352 STATUS
   2353 StringDBSetScope (
   2354   WCHAR   *Scope
   2355   )
   2356 {
   2357   //
   2358   // Free up existing scope memory.
   2359   //
   2360   if (mDBData.CurrentScope != NULL) {
   2361     FREE (mDBData.CurrentScope);
   2362   }
   2363 
   2364   mDBData.CurrentScope = DuplicateString (Scope);
   2365   return STATUS_SUCCESS;
   2366 }
   2367 //
   2368 // We typically don't assign index values to string identifiers
   2369 // until we're ready to write out files. To reduce the size of
   2370 // the output file, re-order the string identifiers to move any
   2371 // unreferenced ones to the end. Then we'll walk the list
   2372 // again to assign string indexes, keeping track of the last
   2373 // one referenced.
   2374 //
   2375 static
   2376 void
   2377 StringDBAssignStringIndexes (
   2378   VOID
   2379   )
   2380 {
   2381   STRING_IDENTIFIER *StrId;
   2382   STRING_IDENTIFIER *FirstUsed;
   2383   STRING_IDENTIFIER *LastUsed;
   2384   STRING_IDENTIFIER *FirstUnused;
   2385   STRING_IDENTIFIER *LastUnused;
   2386   UINT32            Index;
   2387   UINT32            MaxReferenced;
   2388 
   2389   //
   2390   // Create two lists -- used and unused. Then put them together with
   2391   // the unused ones on the end.
   2392   //
   2393   FirstUsed   = NULL;
   2394   LastUsed    = NULL;
   2395   FirstUnused = NULL;
   2396   LastUnused  = NULL;
   2397   StrId       = mDBData.StringIdentifier;
   2398   while (StrId != NULL) {
   2399     if ((StrId->Flags & STRING_FLAGS_REFERENCED) == 0) {
   2400       //
   2401       // Put it on the unused list
   2402       //
   2403       if (FirstUnused == NULL) {
   2404         FirstUnused = StrId;
   2405       } else {
   2406         LastUnused->Next = StrId;
   2407       }
   2408 
   2409       LastUnused        = StrId;
   2410       StrId             = StrId->Next;
   2411       LastUnused->Next  = NULL;
   2412     } else {
   2413       //
   2414       // Put it on the used list
   2415       //
   2416       if (FirstUsed == NULL) {
   2417         FirstUsed = StrId;
   2418       } else {
   2419         LastUsed->Next = StrId;
   2420       }
   2421 
   2422       LastUsed        = StrId;
   2423       StrId           = StrId->Next;
   2424       LastUsed->Next  = NULL;
   2425     }
   2426   }
   2427   //
   2428   // Join the lists
   2429   //
   2430   if (FirstUsed != NULL) {
   2431     mDBData.StringIdentifier  = FirstUsed;
   2432     LastUsed->Next            = FirstUnused;
   2433   } else {
   2434     mDBData.StringIdentifier = FirstUnused;
   2435   }
   2436 
   2437   MaxReferenced = 0;
   2438   Index         = 0;
   2439   for (StrId = mDBData.StringIdentifier; StrId != NULL; StrId = StrId->Next) {
   2440     StrId->Index = Index;
   2441     Index++;
   2442     if (StrId->Flags & STRING_FLAGS_REFERENCED) {
   2443       mDBData.NumStringIdentifiersReferenced = Index;
   2444     }
   2445   }
   2446 
   2447   mDBData.NumStringIdentifiers = Index;
   2448 }
   2449 
   2450 static
   2451 WCHAR *
   2452 DuplicateString (
   2453   WCHAR   *Str
   2454   )
   2455 {
   2456   WCHAR *NewStr;
   2457   if (Str == NULL) {
   2458     return NULL;
   2459   }
   2460 
   2461   NewStr = MALLOC ((wcslen (Str) + 1) * sizeof (WCHAR));
   2462   if (NewStr == NULL) {
   2463     Error (NULL, 0, 0, "memory allocation failure", NULL);
   2464     return NULL;
   2465   }
   2466 
   2467   wcscpy (NewStr, Str);
   2468   return NewStr;
   2469 }
   2470 
   2471 static
   2472 WCHAR *
   2473 AsciiToWchar (
   2474   INT8 *Str
   2475   )
   2476 {
   2477   UINT32  Len;
   2478   WCHAR   *NewStr;
   2479   WCHAR   *Ptr;
   2480 
   2481   Len     = strlen (Str) + 1;
   2482   NewStr  = (WCHAR *) malloc (Len * sizeof (WCHAR));
   2483   for (Ptr = NewStr; *Str != 0; Str++, Ptr++) {
   2484     *Ptr = (UINT16) (UINT8) *Str;
   2485   }
   2486 
   2487   *Ptr = 0;
   2488   return NewStr;
   2489 }
   2490 
   2491 /*****************************************************************************/
   2492 
   2493 /*++
   2494 
   2495 Routine Description:
   2496 
   2497   Create an HII export string pack for the strings in our database.
   2498 
   2499 Arguments:
   2500 
   2501   FileName        - name of the output file to write
   2502 
   2503 Returns:
   2504 
   2505   STATUS
   2506 
   2507 
   2508 --*/
   2509 STATUS
   2510 StringDBCreateHiiExportPack (
   2511   INT8                        *FileName,
   2512   WCHAR_STRING_LIST           *LanguagesOfInterest
   2513   )
   2514 {
   2515   FILE                        *Fptr;
   2516   LANGUAGE_LIST               *Lang;
   2517   STRING_LIST                 *CurrString;
   2518   STRING_LIST                 EmptyString;
   2519   UINT32                      Offset;
   2520   UINT32                      StringIndex;
   2521   UINT32                      TempIndex;
   2522   EFI_HII_STRING_PACK_HEADER  StringPack;
   2523   UINT32                      Len;
   2524   WCHAR                       ZeroString[1];
   2525   WCHAR                       *TempStringPtr;
   2526   WCHAR                       *LangName;
   2527   STRING_IDENTIFIER           *StringIdentifier;
   2528   WCHAR_STRING_LIST           *LOIPtr;
   2529   BOOLEAN                     LanguageOk;
   2530 
   2531 
   2532   if ((Fptr = fopen (FileName, "wb")) == NULL) {
   2533     Error (NULL, 0, 0, FileName, "failed to open output HII export file");
   2534     return STATUS_ERROR;
   2535   }
   2536   //
   2537   // Assign index values to the string identifiers
   2538   //
   2539   StringDBAssignStringIndexes ();
   2540   //
   2541   // If a given string is not defined, then we'll use this one.
   2542   //
   2543   memset (&EmptyString, 0, sizeof (EmptyString));
   2544   EmptyString.Size  = sizeof (ZeroString);
   2545   EmptyString.Str   = ZeroString;
   2546   //
   2547   // Process each language, then each string for each langage
   2548   //
   2549   ZeroString[0] = 0;
   2550   for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {
   2551     //
   2552     // If we have a language list, then make sure this language is in that
   2553     // list.
   2554     //
   2555     LanguageOk  = TRUE;
   2556     if (LanguagesOfInterest != NULL) {
   2557       LanguageOk = FALSE;
   2558       for (LOIPtr = LanguagesOfInterest; LOIPtr != NULL; LOIPtr = LOIPtr->Next) {
   2559         if (wcsncmp (LOIPtr->Str, Lang->LanguageName, LANGUAGE_IDENTIFIER_NAME_LEN) == 0) {
   2560           LanguageOk  = TRUE;
   2561           break;
   2562         }
   2563       }
   2564     }
   2565 
   2566     if (!LanguageOk) {
   2567       continue;
   2568     }
   2569 
   2570     //
   2571     // Process each string for this language. We have to make 3 passes on the strings:
   2572     //   Pass1: computes sizes and fill in the string pack header
   2573     //   Pass2: write the array of offsets
   2574     //   Pass3: write the strings
   2575     //
   2576     //
   2577     // PASS 1: Fill in and print the HII string pack header
   2578     //
   2579     // Compute the size for this language package and write
   2580     // the header out. Each string package contains:
   2581     //   Header
   2582     //   Offset[]  -- an array of offsets to strings, of type RELOFST each
   2583     //   String[]  -- the actual strings themselves
   2584     //
   2585     memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER));
   2586     StringPack.Header.Type        = EFI_HII_STRING;
   2587     StringPack.NumStringPointers  = (UINT16) mDBData.NumStringIdentifiersReferenced;
   2588     LangName                      = Lang->LanguageName;
   2589     //
   2590     // First string is the language name. If we're printing all languages, then
   2591     // it's just the "spa". If we were given a list of languages to print, then it's
   2592     // the "spacat" string. Compute its offset and fill in
   2593     // the info in the header. Since we know the language name string's length,
   2594     // and the printable language name follows it, use that info to fill in the
   2595     // entry for the printable language name as well.
   2596     //
   2597     StringPack.LanguageNameString = (STRING_OFFSET) (sizeof (EFI_HII_STRING_PACK_HEADER) + (mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)));
   2598     StringPack.PrintableLanguageName = (STRING_OFFSET) (StringPack.LanguageNameString + (wcslen (LangName) + 1) * sizeof (WCHAR));
   2599     //
   2600     // Add up the size of all strings so we can fill in our header.
   2601     //
   2602     Len = 0;
   2603     for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
   2604       //
   2605       // For the first string (language name), we print out the "spacat" if they
   2606       // requested it. We set LangName to point to the proper language name string above.
   2607       //
   2608       if (StringIndex == STRING_ID_LANGUAGE_NAME) {
   2609         Len += (wcslen (LangName) + 1) * sizeof (WCHAR);
   2610       } else {
   2611         //
   2612         // Find a string with this language.stringname
   2613         //
   2614         StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
   2615         if (StringIdentifier == NULL) {
   2616           Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
   2617           return STATUS_ERROR;
   2618         }
   2619         //
   2620         // Find a matching string if this string identifier was referenced
   2621         //
   2622         EmptyString.Flags = STRING_FLAGS_UNDEFINED;
   2623         CurrString        = NULL;
   2624         if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
   2625           CurrString = StringDBFindString (
   2626                         Lang->LanguageName,
   2627                         StringIdentifier->StringName,
   2628                         NULL,
   2629                         NULL, // LanguagesOfInterest,
   2630                         NULL
   2631                         );
   2632           //
   2633           // IndirectionList);
   2634           //
   2635           if (NULL == CurrString) {
   2636             //
   2637             // If string for Lang->LanguageName is not found, try to get an English version
   2638             //
   2639             CurrString = StringDBFindString (
   2640                           L"eng",
   2641                           StringIdentifier->StringName,
   2642                           NULL,
   2643                           NULL, // LanguagesOfInterest,
   2644                           NULL
   2645                           );
   2646             //
   2647             // IndirectionList);
   2648             //
   2649           }
   2650         }
   2651 
   2652         if (CurrString == NULL) {
   2653           CurrString = &EmptyString;
   2654           EmptyString.Flags |= StringIdentifier->Flags;
   2655         }
   2656 
   2657         Len += CurrString->Size;
   2658       }
   2659     }
   2660     StringPack.Header.Length =    sizeof (EFI_HII_STRING_PACK_HEADER)
   2661                                 + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)
   2662                                 + Len;
   2663     //
   2664     // Write out the string pack header
   2665     //
   2666     fwrite ((void *) &StringPack, sizeof (StringPack), 1, Fptr);
   2667     //
   2668     // PASS2 : write the offsets
   2669     //
   2670     // Traverse the list of strings again and write the array of offsets. The
   2671     // offset to the first string is the size of the string pack header
   2672     // plus the size of the offsets array. The other strings follow it.
   2673     //
   2674     StringIndex = 0;
   2675     Offset      = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);
   2676     for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
   2677       //
   2678       // Write the offset
   2679       //
   2680       fwrite (&Offset, sizeof (STRING_OFFSET), 1, Fptr);
   2681       //
   2682       // Find the string name
   2683       //
   2684       StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
   2685       if (StringIdentifier == NULL) {
   2686         Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
   2687         return STATUS_ERROR;
   2688       }
   2689       //
   2690       // For the first string (language name), we print out the "spacat" if they
   2691       // requested it. We set LangName to point to the proper language name string above.
   2692       //
   2693       if (StringIndex == STRING_ID_LANGUAGE_NAME) {
   2694         Offset += (wcslen (LangName) + 1) * sizeof (WCHAR);
   2695         CurrString = StringDBFindString (
   2696                       Lang->LanguageName,
   2697                       StringIdentifier->StringName,
   2698                       NULL, // scope
   2699                       NULL,
   2700                       NULL
   2701                       );
   2702       } else {
   2703         //
   2704         // Find a matching string
   2705         //
   2706         CurrString = StringDBFindString (
   2707                       Lang->LanguageName,
   2708                       StringIdentifier->StringName,
   2709                       NULL, // scope
   2710                       NULL, // LanguagesOfInterest,
   2711                       NULL
   2712                       );
   2713         //
   2714         // IndirectionList);
   2715         //
   2716         if (NULL == CurrString) {
   2717           CurrString = StringDBFindString (
   2718                         L"eng",
   2719                         StringIdentifier->StringName,
   2720                         NULL, // scope
   2721                         NULL, // LanguagesOfInterest,
   2722                         NULL
   2723                         );
   2724           //
   2725           // IndirectionList);
   2726           //
   2727         }
   2728 
   2729         EmptyString.LanguageName = Lang->LanguageName;
   2730         if (CurrString == NULL) {
   2731           CurrString        = &EmptyString;
   2732           EmptyString.Flags = STRING_FLAGS_UNDEFINED;
   2733         } else if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) {
   2734           CurrString        = &EmptyString;
   2735           EmptyString.Flags = 0;
   2736         }
   2737 
   2738         Offset += CurrString->Size;
   2739       }
   2740     }
   2741 
   2742     //
   2743     // PASS 3: write the strings themselves.
   2744     //
   2745     Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);
   2746     for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {
   2747       StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);
   2748       if (StringIdentifier == NULL) {
   2749         Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);
   2750         return STATUS_ERROR;
   2751       }
   2752       //
   2753       // For the first string (language name), we print out the "spacat" if they
   2754       // requested it. We set LangName to point to the proper language name string above.
   2755       //
   2756       if (StringIndex == STRING_ID_LANGUAGE_NAME) {
   2757         TempStringPtr = LangName;
   2758       } else {
   2759         //
   2760         // Find a matching string if this string identifier was referenced
   2761         //
   2762         CurrString = NULL;
   2763         if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {
   2764           CurrString = StringDBFindString (
   2765                         Lang->LanguageName,
   2766                         StringIdentifier->StringName,
   2767                         NULL, // scope
   2768                         NULL, // LanguagesOfInterest,
   2769                         NULL
   2770                         );
   2771           //
   2772           // IndirectionList);
   2773           //
   2774           if (NULL == CurrString) {
   2775             CurrString = StringDBFindString (
   2776                           L"eng",
   2777                           StringIdentifier->StringName,
   2778                           NULL, // scope
   2779                           NULL, // LanguagesOfInterest,
   2780                           NULL
   2781                           );
   2782             //
   2783             // IndirectionList);
   2784             //
   2785           }
   2786         }
   2787 
   2788         if (CurrString == NULL) {
   2789           CurrString = &EmptyString;
   2790         }
   2791 
   2792         TempStringPtr = CurrString->Str;
   2793       }
   2794 
   2795       for (TempIndex = 0; TempStringPtr[TempIndex] != 0; TempIndex++) {
   2796         fwrite (&TempStringPtr[TempIndex], sizeof (CHAR16), 1, Fptr);
   2797         Offset += 2;
   2798       }
   2799       //
   2800       // Print NULL WCHAR at the end of this string.
   2801       //
   2802       TempIndex = 0;
   2803       fwrite (&TempIndex, sizeof (CHAR16), 1, Fptr);
   2804       Offset += 2;
   2805     }
   2806     //
   2807     // Sanity check the offset. Make sure our running offset is what we put in the
   2808     // string pack header.
   2809     //
   2810     if (StringPack.Header.Length != Offset) {
   2811       Error (
   2812         __FILE__,
   2813         __LINE__,
   2814         0,
   2815         "application error",
   2816         "stringpack size 0x%X does not match final size 0x%X",
   2817         StringPack.Header.Length,
   2818         Offset
   2819         );
   2820     }
   2821   }
   2822   //
   2823   // Print terminator string pack, closing brace and close the file.
   2824   // The size of 0 triggers to the consumer that this is the end.
   2825   //
   2826   memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER));
   2827   StringPack.Header.Type = EFI_HII_STRING;
   2828   fwrite ((void *) &StringPack, sizeof (StringPack), 1, Fptr);
   2829   fclose (Fptr);
   2830   return STATUS_SUCCESS;
   2831 }
   2832