]> git.proxmox.com Git - mirror_edk2.git/blobdiff - EdkCompatibilityPkg/Sample/Tools/Source/StrGather/StringDB.c
Add in the 1st version of ECP.
[mirror_edk2.git] / EdkCompatibilityPkg / Sample / Tools / Source / StrGather / StringDB.c
diff --git a/EdkCompatibilityPkg/Sample/Tools/Source/StrGather/StringDB.c b/EdkCompatibilityPkg/Sample/Tools/Source/StrGather/StringDB.c
new file mode 100644 (file)
index 0000000..8fa01e8
--- /dev/null
@@ -0,0 +1,2808 @@
+/*++\r
+\r
+Copyright (c) 2004 - 2007, Intel Corporation                                                         \r
+All rights reserved. This program and the accompanying materials                          \r
+are licensed and made available under the terms and conditions of the BSD License         \r
+which accompanies this distribution.  The full text of the license may be found at        \r
+http://opensource.org/licenses/bsd-license.php                                            \r
+                                                                                          \r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     \r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             \r
+\r
+Module Name:\r
+\r
+  StringDB.c\r
+\r
+Abstract:\r
+\r
+  String database implementation\r
+  \r
+--*/\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>  // for tolower()\r
+#include "Tiano.h"\r
+#include "EfiUtilityMsgs.h"\r
+#include "StrGather.h"\r
+#include "StringDb.h"\r
+#include "EfiInternalFormRepresentation.h"\r
+\r
+#include EFI_PROTOCOL_DEFINITION (Hii)\r
+\r
+typedef CHAR16  WCHAR;\r
+#define STRING_OFFSET RELOFST\r
+\r
+#define STRING_DB_KEY (('S' << 24) | ('D' << 16) | ('B' << 8) | 'K')\r
+//\r
+// Version supported by this tool\r
+//\r
+#define STRING_DB_VERSION             0x00010000\r
+\r
+#define STRING_DB_MAJOR_VERSION_MASK  0xFFFF0000\r
+#define STRING_DB_MINOR_VERSION_MASK  0x0000FFFF\r
+\r
+#define DEFINE_STR                    L"// #define"\r
+\r
+#define LANGUAGE_CODE_WIDTH           4\r
+//\r
+// This is the header that gets written to the top of the\r
+// output binary database file.\r
+//\r
+typedef struct {\r
+  UINT32  Key;\r
+  UINT32  HeaderSize;\r
+  UINT32  Version;\r
+  UINT32  NumStringIdenfiers;\r
+  UINT32  StringIdentifiersSize;\r
+  UINT32  NumLanguages;\r
+} STRING_DB_HEADER;\r
+\r
+//\r
+// When we write out data to the database, we have a UINT16 identifier, which\r
+// indicates what follows, followed by the data. Here's the structure.\r
+//\r
+typedef struct {\r
+  UINT16  DataType;\r
+  UINT16  Reserved;\r
+} DB_DATA_ITEM_HEADER;\r
+\r
+#define DB_DATA_TYPE_INVALID              0x0000\r
+#define DB_DATA_TYPE_STRING_IDENTIFIER    0x0001\r
+#define DB_DATA_TYPE_LANGUAGE_DEFINITION  0x0002\r
+#define DB_DATA_TYPE_STRING_DEFINITION    0x0003\r
+#define DB_DATA_TYPE_LAST                 DB_DATA_TYPE_STRING_DEFINITION\r
+\r
+//\r
+// We have to keep track of a list of languages, each of which has its own\r
+// list of strings. Define a structure to keep track of all languages and\r
+// their list of strings.\r
+//\r
+typedef struct _STRING_LIST {\r
+  struct _STRING_LIST *Next;\r
+  UINT32              Size;         // number of bytes in string, including null terminator\r
+  WCHAR               *LanguageName;\r
+  WCHAR               *StringName;  // for example STR_ID_TEXT1\r
+  WCHAR               *Scope;       //\r
+  WCHAR               *Str;         // the actual string\r
+  UINT16              Flags;        // properties of this string (used, undefined)\r
+} STRING_LIST;\r
+\r
+typedef struct _LANGUAGE_LIST {\r
+  struct _LANGUAGE_LIST *Next;\r
+  WCHAR                 LanguageName[4];\r
+  WCHAR                 *PrintableLanguageName;\r
+  STRING_LIST           *String;\r
+  STRING_LIST           *LastString;\r
+} LANGUAGE_LIST;\r
+\r
+//\r
+// We also keep track of all the string identifier names, which we assign unique\r
+// values to. Create a structure to keep track of them all.\r
+//\r
+typedef struct _STRING_IDENTIFIER {\r
+  struct _STRING_IDENTIFIER *Next;\r
+  UINT32                    Index;  // only need 16 bits, but makes it easier with UINT32\r
+  WCHAR                     *StringName;\r
+  UINT16                    Flags;  // if someone referenced it via STRING_TOKEN()\r
+} STRING_IDENTIFIER;\r
+//\r
+// Keep our globals in this structure to be as modular as possible.\r
+//\r
+typedef struct {\r
+  FILE              *StringDBFptr;\r
+  LANGUAGE_LIST     *LanguageList;\r
+  LANGUAGE_LIST     *LastLanguageList;\r
+  LANGUAGE_LIST     *CurrentLanguage;         // keep track of the last language they used\r
+  STRING_IDENTIFIER *StringIdentifier;\r
+  STRING_IDENTIFIER *LastStringIdentifier;\r
+  UINT8             *StringDBFileName;\r
+  UINT32            NumStringIdentifiers;\r
+  UINT32            NumStringIdentifiersReferenced;\r
+  STRING_IDENTIFIER *CurrentStringIdentifier; // keep track of the last string identifier they added\r
+  WCHAR             *CurrentScope;\r
+} STRING_DB_DATA;\r
+\r
+static STRING_DB_DATA mDBData;\r
+\r
+static const char     *mSourceFileHeader[] = {\r
+  "//",\r
+  "//  DO NOT EDIT -- auto-generated file",\r
+  "//",\r
+  "//  This file is generated by the string gather utility",\r
+  "//",\r
+  NULL\r
+};\r
+\r
+static\r
+STRING_LIST           *\r
+StringDBFindString (\r
+  WCHAR                       *LanguageName,\r
+  WCHAR                       *StringName,\r
+  WCHAR                       *Scope,\r
+  WCHAR_STRING_LIST           *LanguagesOfInterest,\r
+  WCHAR_MATCHING_STRING_LIST  *IndirectionList\r
+  );\r
+\r
+static\r
+STRING_IDENTIFIER     *\r
+StringDBFindStringIdentifierByName (\r
+  WCHAR *Name\r
+  );\r
+\r
+static\r
+STRING_IDENTIFIER     *\r
+StringDBFindStringIdentifierByIndex (\r
+  UINT32    Index\r
+  );\r
+\r
+static\r
+LANGUAGE_LIST         *\r
+StringDBFindLanguageList (\r
+  WCHAR *LanguageName\r
+  );\r
+\r
+static\r
+void\r
+StringDBWriteStandardFileHeader (\r
+  FILE *OutFptr\r
+  );\r
+\r
+static\r
+WCHAR                 *\r
+AsciiToWchar (\r
+  INT8 *Str\r
+  );\r
+\r
+static\r
+WCHAR                 *\r
+DuplicateString (\r
+  WCHAR   *Str\r
+  );\r
+\r
+static\r
+STATUS\r
+StringDBWriteStringIdentifier (\r
+  FILE                *DBFptr,\r
+  UINT16              StringId,\r
+  UINT16              Flags,\r
+  WCHAR               *IdentifierName\r
+  );\r
+\r
+static\r
+STATUS\r
+StringDBReadStringIdentifier (\r
+  FILE                *DBFptr\r
+  );\r
+\r
+static\r
+STATUS\r
+StringDBWriteLanguageDefinition (\r
+  FILE            *DBFptr,\r
+  WCHAR           *LanguageName,\r
+  WCHAR           *PrintableLanguageName\r
+  );\r
+\r
+static\r
+STATUS\r
+StringDBReadLanguageDefinition (\r
+  FILE            *DBFptr\r
+  );\r
+\r
+static\r
+STATUS\r
+StringDBWriteString (\r
+  FILE            *DBFptr,\r
+  UINT16          Flags,\r
+  WCHAR           *Language,\r
+  WCHAR           *StringName,\r
+  WCHAR           *Scope,\r
+  WCHAR           *Str\r
+  );\r
+\r
+static\r
+STATUS\r
+StringDBReadString (\r
+  FILE            *DBFptr\r
+  );\r
+\r
+static\r
+STATUS\r
+StringDBReadGenericString (\r
+  FILE      *DBFptr,\r
+  UINT16    *Size,\r
+  WCHAR     **Str\r
+  );\r
+\r
+static\r
+STATUS\r
+StringDBWriteGenericString (\r
+  FILE      *DBFptr,\r
+  WCHAR     *Str\r
+  );\r
+\r
+static\r
+void\r
+StringDBAssignStringIndexes (\r
+  VOID\r
+  );\r
+\r
+/*****************************************************************************/\r
+\r
+/*++\r
+\r
+Routine Description:\r
+  Constructor function for the string database handler.\r
+\r
+Arguments:\r
+  None.\r
+\r
+Returns:\r
+  None.\r
+\r
+--*/\r
+void\r
+StringDBConstructor (\r
+  VOID\r
+  )\r
+{\r
+  memset ((char *) &mDBData, 0, sizeof (STRING_DB_DATA));\r
+  mDBData.CurrentScope = DuplicateString (L"NULL");\r
+}\r
+\r
+/*****************************************************************************/\r
+\r
+/*++\r
+\r
+Routine Description:\r
+  Destructor function for the string database handler.\r
+\r
+Arguments:\r
+  None.\r
+\r
+Returns:\r
+  None.\r
+\r
+--*/\r
+void\r
+StringDBDestructor (\r
+  VOID\r
+  )\r
+{\r
+  LANGUAGE_LIST     *NextLang;\r
+  STRING_LIST       *NextStr;\r
+  STRING_IDENTIFIER *NextIdentifier;\r
+  //\r
+  // Close the database file if it's open\r
+  //\r
+  if (mDBData.StringDBFptr != NULL) {\r
+    fclose (mDBData.StringDBFptr);\r
+    mDBData.StringDBFptr = NULL;\r
+  }\r
+  //\r
+  // If we've allocated any strings/languages, free them up\r
+  //\r
+  while (mDBData.LanguageList != NULL) {\r
+    NextLang = mDBData.LanguageList->Next;\r
+    //\r
+    // Free up all strings for this language\r
+    //\r
+    while (mDBData.LanguageList->String != NULL) {\r
+      NextStr = mDBData.LanguageList->String->Next;\r
+      FREE (mDBData.LanguageList->String->Str);\r
+      FREE (mDBData.LanguageList->String);\r
+      mDBData.LanguageList->String = NextStr;\r
+    }\r
+\r
+    FREE (mDBData.LanguageList->PrintableLanguageName);\r
+    FREE (mDBData.LanguageList);\r
+    mDBData.LanguageList = NextLang;\r
+  }\r
+  //\r
+  // Free up string identifiers\r
+  //\r
+  while (mDBData.StringIdentifier != NULL) {\r
+    NextIdentifier = mDBData.StringIdentifier->Next;\r
+    FREE (mDBData.StringIdentifier->StringName);\r
+    FREE (mDBData.StringIdentifier);\r
+    mDBData.StringIdentifier = NextIdentifier;\r
+  }\r
+  //\r
+  // Free the filename\r
+  //\r
+  if (mDBData.StringDBFileName != NULL) {\r
+    FREE (mDBData.StringDBFileName);\r
+    mDBData.StringDBFileName = NULL;\r
+  }\r
+  //\r
+  // We save a copy of the scope, so free it up if we\r
+  // have one.\r
+  //\r
+  if (mDBData.CurrentScope != NULL) {\r
+    FREE (mDBData.CurrentScope);\r
+    mDBData.CurrentScope = NULL;\r
+  }\r
+}\r
+\r
+/*****************************************************************************/\r
+\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  Dump the contents of a database to an output C file.\r
+\r
+Arguments:\r
+\r
+  FileName        - name of the output file to write \r
+  BaseName        - used for the name of the C array defined\r
+  Languages       - list of languages of interest\r
+\r
+Returns:\r
+\r
+  STATUS\r
+\r
+Notes:\r
+\r
+  Languages is a pointer to a linked list of languages specified on\r
+  the command line. Format is "eng" and "spa+cat". For this, print\r
+  the strings for eng. Print the strings for spa too, but if one is\r
+  missing look for a cat string and print if it it exists.\r
+\r
+--*/\r
+STATUS\r
+StringDBDumpCStrings (\r
+  INT8                        *FileName,\r
+  INT8                        *BaseName,\r
+  WCHAR_STRING_LIST           *LanguagesOfInterest,\r
+  WCHAR_MATCHING_STRING_LIST  *IndirectionList\r
+  )\r
+{\r
+  FILE                        *Fptr;\r
+  LANGUAGE_LIST               *Lang;\r
+  STRING_LIST                 *CurrString;\r
+  STRING_LIST                 EmptyString;\r
+  UINT32                      Offset;\r
+  UINT32                      StringIndex;\r
+  UINT32                      TempIndex;\r
+  UINT32                      BytesThisLine;\r
+  EFI_HII_STRING_PACK_HEADER  StringPack;\r
+  UINT8                       *Ptr;\r
+  UINT32                      Len;\r
+  WCHAR                       ZeroString[1];\r
+  WCHAR_STRING_LIST           *LOIPtr;\r
+  BOOLEAN                     LanguageOk;\r
+  WCHAR                       *TempStringPtr;\r
+  WCHAR                       *LangName;\r
+  STRING_IDENTIFIER           *StringIdentifier;\r
+\r
+  if ((Fptr = fopen (FileName, "w")) == NULL) {\r
+    Error (NULL, 0, 0, FileName, "failed to open output C string file");\r
+    return STATUS_ERROR;\r
+  }\r
+  //\r
+  // Assign index values to the string identifiers\r
+  //\r
+  StringDBAssignStringIndexes ();\r
+  //\r
+  // Write the standard header to the output file, then the structure\r
+  // definition header.\r
+  //\r
+  StringDBWriteStandardFileHeader (Fptr);\r
+  fprintf (Fptr, "\nunsigned char %s[] = {\n", BaseName);\r
+  //\r
+  // If a given string is not defined, then we'll use this one.\r
+  //\r
+  memset (&EmptyString, 0, sizeof (EmptyString));\r
+  EmptyString.Size  = sizeof (ZeroString);\r
+  EmptyString.Str   = ZeroString;\r
+  //\r
+  // Process each language, then each string for each langage\r
+  //\r
+  ZeroString[0] = 0;\r
+  for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {\r
+    //\r
+    // If we have a language list, then make sure this language is in that\r
+    // list.\r
+    //\r
+    LanguageOk  = TRUE;\r
+    LangName    = Lang->LanguageName;\r
+    if (LanguagesOfInterest != NULL) {\r
+      LanguageOk = FALSE;\r
+      for (LOIPtr = LanguagesOfInterest; LOIPtr != NULL; LOIPtr = LOIPtr->Next) {\r
+        if (wcsncmp (LOIPtr->Str, Lang->LanguageName, LANGUAGE_IDENTIFIER_NAME_LEN) == 0) {\r
+          LangName    = LOIPtr->Str;\r
+          LanguageOk  = TRUE;\r
+          break;\r
+        }\r
+      }\r
+    }\r
+\r
+    if (!LanguageOk) {\r
+      continue;\r
+    }\r
+    //\r
+    // Process each string for this language. We have to make 3 passes on the strings:\r
+    //   Pass1: computes sizes and fill in the string pack header\r
+    //   Pass2: write the array of offsets\r
+    //   Pass3: write the strings\r
+    //\r
+    //\r
+    // PASS 1: Fill in and print the HII string pack header\r
+    //\r
+    // Compute the size for this language package and write\r
+    // the header out. Each string package contains:\r
+    //   Header\r
+    //   Offset[]  -- an array of offsets to strings, of type RELOFST each\r
+    //   String[]  -- the actual strings themselves\r
+    //\r
+    fprintf (\r
+      Fptr,\r
+      "\n//******************************************************************************"\r
+      "\n// Start of string definitions for %S/%S",\r
+      Lang->LanguageName,\r
+      Lang->PrintableLanguageName\r
+      );\r
+    memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER));\r
+    StringPack.Header.Type        = EFI_HII_STRING;\r
+    StringPack.NumStringPointers  = (UINT16) mDBData.NumStringIdentifiersReferenced;\r
+    //\r
+    // First string is the language name. If we're printing all languages, then\r
+    // it's just the "spa". If we were given a list of languages to print, then it's\r
+    // the "spacat" string. Compute its offset and fill in\r
+    // the info in the header. Since we know the language name string's length,\r
+    // and the printable language name follows it, use that info to fill in the\r
+    // entry for the printable language name as well.\r
+    //\r
+    StringPack.LanguageNameString = (STRING_OFFSET) (sizeof (EFI_HII_STRING_PACK_HEADER) + (mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)));\r
+    StringPack.PrintableLanguageName = (STRING_OFFSET) (StringPack.LanguageNameString + (wcslen (LangName) + 1) * sizeof (WCHAR));\r
+    //\r
+    // Add up the size of all strings so we can fill in our header.\r
+    //\r
+    Len = 0;\r
+    for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {\r
+      //\r
+      // For the first string (language name), we print out the "spacat" if they\r
+      // requested it. We set LangName to point to the proper language name string above.\r
+      //\r
+      if (StringIndex == STRING_ID_LANGUAGE_NAME) {\r
+        Len += (wcslen (LangName) + 1) * sizeof (WCHAR);\r
+      } else {\r
+        //\r
+        // Find a string with this language.stringname\r
+        //\r
+        StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);\r
+        if (StringIdentifier == NULL) {\r
+          Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);\r
+          return STATUS_ERROR;\r
+        }\r
+        //\r
+        // Find a matching string if this string identifier was referenced\r
+        //\r
+        EmptyString.Flags = STRING_FLAGS_UNDEFINED;\r
+        CurrString        = NULL;\r
+        if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {\r
+          CurrString = StringDBFindString (\r
+                        Lang->LanguageName,\r
+                        StringIdentifier->StringName,\r
+                        NULL,\r
+                        LanguagesOfInterest,\r
+                        IndirectionList\r
+                        );\r
+          if (NULL == CurrString) {\r
+            //\r
+            // If string for Lang->LanguageName is not found, try to get an English version\r
+            //\r
+            CurrString = StringDBFindString (\r
+                          L"eng",\r
+                          StringIdentifier->StringName,\r
+                          NULL,\r
+                          LanguagesOfInterest,\r
+                          IndirectionList\r
+                          );\r
+          }\r
+        }\r
+\r
+        if (CurrString == NULL) {\r
+          CurrString = &EmptyString;\r
+          EmptyString.Flags |= StringIdentifier->Flags;\r
+        }\r
+\r
+        Len += CurrString->Size;\r
+      }\r
+    }\r
+    StringPack.Header.Length =    sizeof (EFI_HII_STRING_PACK_HEADER) \r
+                                + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET) \r
+                                + Len;\r
+    //\r
+    // Write out the header one byte at a time\r
+    //\r
+    Ptr = (UINT8 *) &StringPack;\r
+    for (TempIndex = 0; TempIndex < sizeof (EFI_HII_STRING_PACK_HEADER); TempIndex++, Ptr++) {\r
+      if ((TempIndex & 0x07) == 0) {\r
+        fprintf (Fptr, "\n  ");\r
+      }\r
+\r
+      fprintf (Fptr, "0x%02X, ", (UINT32) *Ptr);\r
+    }\r
+\r
+    fprintf (Fptr, "\n  // offset 0x%X\n", sizeof (StringPack));\r
+    //\r
+    // PASS2 : write the offsets\r
+    //\r
+    // Traverse the list of strings again and write the array of offsets. The\r
+    // offset to the first string is the size of the string pack header\r
+    // plus the size of the offsets array. The other strings follow it.\r
+    //\r
+    StringIndex = 0;\r
+    Offset      = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);\r
+    for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {\r
+      //\r
+      // Write the offset, followed by a useful comment\r
+      //\r
+      fprintf (Fptr, "  ");\r
+      Ptr = (UINT8 *) &Offset;\r
+      for (TempIndex = 0; TempIndex < sizeof (STRING_OFFSET); TempIndex++) {\r
+        fprintf (Fptr, "0x%02X, ", (UINT32) Ptr[TempIndex]);\r
+      }\r
+      //\r
+      // Find the string name\r
+      //\r
+      StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);\r
+      if (StringIdentifier == NULL) {\r
+        Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      fprintf (Fptr, " // offset to string %S (0x%04X)", StringIdentifier->StringName, StringIndex);\r
+      //\r
+      // For the first string (language name), we print out the "spacat" if they\r
+      // requested it. We set LangName to point to the proper language name string above.\r
+      //\r
+      if (StringIndex == STRING_ID_LANGUAGE_NAME) {\r
+        Offset += (wcslen (LangName) + 1) * sizeof (WCHAR);\r
+        CurrString = StringDBFindString (\r
+                      Lang->LanguageName,\r
+                      StringIdentifier->StringName,\r
+                      NULL, // scope\r
+                      NULL,\r
+                      NULL\r
+                      );\r
+      } else {\r
+        //\r
+        // Find a matching string\r
+        //\r
+        CurrString = StringDBFindString (\r
+                      Lang->LanguageName,\r
+                      StringIdentifier->StringName,\r
+                      NULL,   // scope\r
+                      LanguagesOfInterest,\r
+                      IndirectionList\r
+                      );\r
+\r
+        if (NULL == CurrString) {\r
+          CurrString = StringDBFindString (\r
+                        L"eng",\r
+                        StringIdentifier->StringName,\r
+                        NULL, // scope\r
+                        LanguagesOfInterest,\r
+                        IndirectionList\r
+                        );\r
+        }\r
+\r
+        EmptyString.LanguageName = Lang->LanguageName;\r
+        if (CurrString == NULL) {\r
+          CurrString        = &EmptyString;\r
+          EmptyString.Flags = STRING_FLAGS_UNDEFINED;\r
+        } else if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) {\r
+          CurrString        = &EmptyString;\r
+          EmptyString.Flags = 0;\r
+        }\r
+\r
+        Offset += CurrString->Size;\r
+      }\r
+      //\r
+      // Print useful info about this string\r
+      //\r
+      if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) {\r
+        fprintf (Fptr, " - not referenced");\r
+      }\r
+\r
+      if (CurrString->Flags & STRING_FLAGS_UNDEFINED) {\r
+        fprintf (Fptr, " - not defined for this language");\r
+      } else if (wcscmp (CurrString->LanguageName, Lang->LanguageName) != 0) {\r
+        fprintf (\r
+          Fptr,\r
+          " - not defined for this language -- using secondary language %S definition",\r
+          CurrString->LanguageName\r
+          );\r
+      }\r
+\r
+      fprintf (Fptr, "\n");\r
+    }\r
+    //\r
+    // For unreferenced string identifiers, print a message that they are not referenced anywhere\r
+    //\r
+    while (StringIndex < mDBData.NumStringIdentifiers) {\r
+      StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);\r
+      if (StringIdentifier != NULL) {\r
+        fprintf (Fptr, "  // %S not referenced\n", StringIdentifier->StringName);\r
+      }\r
+\r
+      StringIndex++;\r
+    }\r
+\r
+    //\r
+    // PASS 3: write the strings themselves.\r
+    // Keep track of how many bytes we write per line because some editors\r
+    // (Visual Studio for instance) can't handle too long of lines.\r
+    //\r
+    Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);\r
+    for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {\r
+      StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);\r
+      if (StringIdentifier == NULL) {\r
+        Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);\r
+        return STATUS_ERROR;\r
+      }\r
+\r
+      fprintf (Fptr, "  // string %S offset 0x%08X\n  ", StringIdentifier->StringName, Offset);\r
+      //\r
+      // For the first string (language name), we print out the "spacat" if they\r
+      // requested it. We set LangName to point to the proper language name string above.\r
+      //\r
+      if (StringIndex == STRING_ID_LANGUAGE_NAME) {\r
+        TempStringPtr = LangName;\r
+      } else {\r
+        //\r
+        // Find a matching string if this string identifier was referenced\r
+        //\r
+        CurrString = NULL;\r
+        if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {\r
+          CurrString = StringDBFindString (\r
+                        Lang->LanguageName,\r
+                        StringIdentifier->StringName,\r
+                        NULL,   // scope\r
+                        LanguagesOfInterest,\r
+                        IndirectionList\r
+                        );\r
+          if (NULL == CurrString) {\r
+            CurrString = StringDBFindString (\r
+                          L"eng",\r
+                          StringIdentifier->StringName,\r
+                          NULL, // scope\r
+                          LanguagesOfInterest,\r
+                          IndirectionList\r
+                          );\r
+          }\r
+        }\r
+\r
+        if (CurrString == NULL) {\r
+          CurrString = &EmptyString;\r
+        }\r
+\r
+        TempStringPtr = CurrString->Str;\r
+      }\r
+\r
+      BytesThisLine = 0;\r
+      for (TempIndex = 0; TempStringPtr[TempIndex] != 0; TempIndex++) {\r
+        fprintf (\r
+          Fptr,\r
+          "0x%02X, 0x%02X, ",\r
+          (UINT32) TempStringPtr[TempIndex] & 0xFF,\r
+          (UINT32) ((TempStringPtr[TempIndex] >> 8) & 0xFF)\r
+          );\r
+        BytesThisLine += 2;\r
+        Offset += 2;\r
+        //\r
+        // Let's say we only allow 14 per line\r
+        //\r
+        if (BytesThisLine > 14) {\r
+          fprintf (Fptr, "\n  ");\r
+          BytesThisLine = 0;\r
+        }\r
+      }\r
+      //\r
+      // Print NULL WCHAR at the end of this string.\r
+      //\r
+      fprintf (Fptr, "0x00, 0x00,\n");\r
+      Offset += 2;\r
+    }\r
+    //\r
+    // Sanity check the offset. Make sure our running offset is what we put in the\r
+    // string pack header.\r
+    //\r
+    if (StringPack.Header.Length != Offset) {\r
+      Error (\r
+        __FILE__,\r
+        __LINE__,\r
+        0,\r
+        "application error",\r
+        "stringpack size 0x%X does not match final size 0x%X",\r
+        StringPack.Header.Length,\r
+        Offset\r
+        );\r
+    }\r
+  }\r
+  //\r
+  // Print terminator string pack, closing brace and close the file.\r
+  // The size of 0 triggers to the consumer that this is the end.\r
+  //\r
+  memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER));\r
+  StringPack.Header.Type  = EFI_HII_STRING;\r
+  Ptr                     = (UINT8 *) &StringPack;\r
+  fprintf (Fptr, "\n  // strings terminator pack");\r
+  for (TempIndex = 0; TempIndex < sizeof (StringPack); TempIndex++, Ptr++) {\r
+    if ((TempIndex & 0x0F) == 0) {\r
+      fprintf (Fptr, "\n  ");\r
+    }\r
+\r
+    fprintf (Fptr, "0x%02X, ", (UINT32) *Ptr);\r
+  }\r
+\r
+  fprintf (Fptr, "\n};\n");\r
+  fclose (Fptr);\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+/*****************************************************************************/\r
+\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  Dump the #define string names\r
+\r
+Arguments:\r
+\r
+  FileName        - name of the output file to write \r
+  BaseName        - used for the protection #ifndef/#endif \r
+\r
+Returns:\r
+\r
+  STATUS\r
+\r
+--*/\r
+STATUS\r
+StringDBDumpStringDefines (\r
+  INT8 *FileName,\r
+  INT8 *BaseName\r
+  )\r
+{\r
+  FILE              *Fptr;\r
+  STRING_IDENTIFIER *Identifier;\r
+  INT8              CopyBaseName[100];\r
+  UINT32            Index;\r
+  const INT8        *StrDefHeader[] = {\r
+    "#ifndef _%s_STRINGS_DEFINE_H_\n",\r
+    "#define _%s_STRINGS_DEFINE_H_\n\n",\r
+    NULL\r
+  };\r
+\r
+  if ((Fptr = fopen (FileName, "w")) == NULL) {\r
+    Error (NULL, 0, 0, FileName, "failed to open output string defines file");\r
+    return STATUS_ERROR;\r
+  }\r
+  //\r
+  // Get the base source filename and convert to uppercase.\r
+  //\r
+  if (sizeof (CopyBaseName) <= strlen (BaseName) + 1) {\r
+    Error (NULL, 0, 0, "application error", "StringDBDumpStringDefines() string length insufficient");\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  strcpy (CopyBaseName, BaseName);\r
+  for (Index = 0; CopyBaseName[Index] != 0; Index++) {\r
+    if (islower (CopyBaseName[Index])) {\r
+      CopyBaseName[Index] = (INT8) toupper (CopyBaseName[Index]);\r
+    }\r
+  }\r
+  //\r
+  // Assign index values to the string identifiers\r
+  //\r
+  StringDBAssignStringIndexes ();\r
+  //\r
+  // Write the standard header to the output file, and then the\r
+  // protective #ifndef.\r
+  //\r
+  StringDBWriteStandardFileHeader (Fptr);\r
+  for (Index = 0; StrDefHeader[Index] != NULL; Index++) {\r
+    fprintf (Fptr, StrDefHeader[Index], CopyBaseName);\r
+  }\r
+  //\r
+  // Print all the #defines for the string identifiers. Print identifiers\r
+  // whose names start with '$' as comments. Add comments for string\r
+  // identifiers not used as well.\r
+  //\r
+  Identifier = mDBData.StringIdentifier;\r
+  while (Identifier != NULL) {\r
+    if (Identifier->StringName[0] == L'$') {\r
+      fprintf (Fptr, "// ");\r
+    }\r
+\r
+    if (Identifier->Flags & STRING_FLAGS_REFERENCED) {\r
+      fprintf (Fptr, "#define %-40S 0x%04X\n", Identifier->StringName, Identifier->Index);\r
+    } else {\r
+      fprintf (Fptr, "//#define %-40S 0x%04X // not referenced\n", Identifier->StringName, Identifier->Index);\r
+    }\r
+\r
+    Identifier = Identifier->Next;\r
+  }\r
+\r
+  fprintf (Fptr, "\n#endif\n");\r
+  fclose (Fptr);\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+/*****************************************************************************/\r
+\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  Add a string identifier to the database.\r
+\r
+Arguments:\r
+\r
+  StringName      - name of the string identifier. For example "STR_MY_STRING"\r
+  NewId           - if an ID has been assigned\r
+  Flags           - characteristics for the identifier\r
+\r
+Returns:\r
+\r
+  STATUS\r
+\r
+--*/\r
+STATUS\r
+StringDBAddStringIdentifier (\r
+  WCHAR     *StringName,\r
+  UINT16    *NewId,\r
+  UINT16    Flags\r
+  )\r
+{\r
+  STRING_IDENTIFIER *StringIdentifier;\r
+  STATUS            Status;\r
+  //\r
+  // If it was already used for some other language, then we don't\r
+  // need to add it. But set it to the current string identifier.\r
+  // The referenced bit is sticky.\r
+  //\r
+  Status            = STATUS_SUCCESS;\r
+  StringIdentifier  = StringDBFindStringIdentifierByName (StringName);\r
+  if (StringIdentifier != NULL) {\r
+    if (Flags & STRING_FLAGS_REFERENCED) {\r
+      StringIdentifier->Flags |= STRING_FLAGS_REFERENCED;\r
+    }\r
+\r
+    mDBData.CurrentStringIdentifier = StringIdentifier;\r
+    *NewId                          = (UINT16) StringIdentifier->Index;\r
+    return Status;\r
+  }\r
+\r
+  StringIdentifier = (STRING_IDENTIFIER *) MALLOC (sizeof (STRING_IDENTIFIER));\r
+  if (StringIdentifier == NULL) {\r
+    Error (NULL, 0, 0, NULL, "memory allocation error");\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  memset ((char *) StringIdentifier, 0, sizeof (STRING_IDENTIFIER));\r
+  StringIdentifier->StringName = (WCHAR *) malloc ((wcslen (StringName) + 1) * sizeof (WCHAR));\r
+  if (StringIdentifier->StringName == NULL) {\r
+    Error (NULL, 0, 0, NULL, "memory allocation error");\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  wcscpy (StringIdentifier->StringName, StringName);\r
+  if (*NewId != STRING_ID_INVALID) {\r
+    StringIdentifier->Index = *NewId;\r
+    StringIdentifier->Flags |= STRING_FLAGS_INDEX_ASSIGNED;\r
+    if (mDBData.NumStringIdentifiers <= StringIdentifier->Index) {\r
+      mDBData.NumStringIdentifiers = StringIdentifier->Index + 1;\r
+    }\r
+  } else {\r
+    StringIdentifier->Index = mDBData.NumStringIdentifiers++;\r
+  }\r
+\r
+  StringIdentifier->Flags |= Flags;\r
+  //\r
+  // Add it to our list of string identifiers\r
+  //\r
+  if (mDBData.StringIdentifier == NULL) {\r
+    mDBData.StringIdentifier = StringIdentifier;\r
+  } else {\r
+    mDBData.LastStringIdentifier->Next = StringIdentifier;\r
+  }\r
+\r
+  mDBData.LastStringIdentifier    = StringIdentifier;\r
+  mDBData.CurrentStringIdentifier = StringIdentifier;\r
+  *NewId                          = (UINT16) StringIdentifier->Index;\r
+  return Status;\r
+}\r
+\r
+/*****************************************************************************/\r
+\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  Add a new string to the database.\r
+\r
+Arguments:\r
+\r
+  LanguageName    - "eng" or "spa" language name\r
+  StringName      - "STR_MY_TEXT" string name\r
+  Scope           - from the #scope statements in the string file\r
+  Format          - if we should format the string\r
+  Flags           - characteristic flags for the string\r
+\r
+Returns:\r
+\r
+  STATUS\r
+\r
+Notes:\r
+\r
+  Several of the fields can be "inherited" from the previous calls to\r
+  our database functions. For example, if scope is NULL here, then\r
+  we'll use the previous setting.\r
+\r
+--*/\r
+STATUS\r
+StringDBAddString (\r
+  WCHAR   *LanguageName,\r
+  WCHAR   *StringName,\r
+  WCHAR   *Scope,\r
+  WCHAR   *String,\r
+  BOOLEAN Format,\r
+  UINT16  Flags\r
+  )\r
+{\r
+  LANGUAGE_LIST     *Lang;\r
+  UINT32            Size;\r
+  STRING_LIST       *Str;\r
+  UINT16            StringIndex;\r
+  WCHAR             TempLangName[4];\r
+  STRING_IDENTIFIER *StringIdentifier;\r
+\r
+  //\r
+  // Check that language name is exactly 3 characters, or emit an error.\r
+  // Truncate at 3 if it's longer, or make it 3 if it's shorter.\r
+  //\r
+  if (LanguageName != NULL) {\r
+    Size = wcslen (LanguageName);\r
+    if (Size != 3) {\r
+      ParserError (0, "invalid length for language name", "%S", LanguageName);\r
+      if (Size > 3) {\r
+        LanguageName[3] = 0;\r
+      } else {\r
+        //\r
+        // Make a local copy of the language name string, and extend to\r
+        // 3 characters since we make assumptions elsewhere in this program\r
+        // on the length.\r
+        //\r
+        wcscpy (TempLangName, LanguageName);\r
+        for (; Size < 3; Size++) {\r
+          TempLangName[Size] = L'?';\r
+        }\r
+\r
+        TempLangName[3] = 0;\r
+        LanguageName    = TempLangName;\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // If they specified a language, make sure they've defined it already\r
+  // via a #langdef statement. Otherwise use the current default language.\r
+  //\r
+  if (LanguageName != NULL) {\r
+    Lang = StringDBFindLanguageList (LanguageName);\r
+    if (Lang == NULL) {\r
+      ParserError (0, "language not defined", "%S", LanguageName);\r
+      return STATUS_ERROR;\r
+    } else {\r
+      StringDBSetCurrentLanguage (LanguageName);\r
+    }\r
+  } else {\r
+    Lang = mDBData.CurrentLanguage;\r
+    if (Lang == NULL) {\r
+      //\r
+      // Have to call SetLanguage() first\r
+      //\r
+      ParserError (0, "no language defined", "%S", StringName);\r
+      return STATUS_ERROR;\r
+    }\r
+  }\r
+  //\r
+  // If they didn't define a string identifier, use the last string identifier\r
+  // added.\r
+  //\r
+  if (StringName == NULL) {\r
+    StringName = mDBData.CurrentStringIdentifier->StringName;\r
+    if (StringName == NULL) {\r
+      ParserError (0, "no string identifier previously specified", NULL);\r
+      return STATUS_ERROR;\r
+    }\r
+  }\r
+  //\r
+  // If scope was not specified, use the default setting\r
+  //\r
+  if (Scope != NULL) {\r
+    Scope = DuplicateString (Scope);\r
+  } else {\r
+    Scope = DuplicateString (mDBData.CurrentScope);\r
+  }\r
+  //\r
+  // printf ("Adding string: %S.%S.%S\n", Lang->LanguageName, StringName, Scope);\r
+  //\r
+  // Check for duplicates for this Language.StringName.Scope. Allow multiple\r
+  // definitions of the language name and printable language name, since the\r
+  // user does not specifically define them.\r
+  //\r
+  if (StringDBFindString (Lang->LanguageName, StringName, Scope, NULL, NULL) != NULL) {\r
+    if ((wcscmp (StringName, LANGUAGE_NAME_STRING_NAME) == 0) &&\r
+        (wcscmp (StringName, PRINTABLE_LANGUAGE_NAME_STRING_NAME) == 0)\r
+        ) {\r
+      ParserError (\r
+        0,\r
+        "string multiply defined",\r
+        "Language.Name.Scope = %S.%S.%S",\r
+        Lang->LanguageName,\r
+        StringName,\r
+        Scope\r
+        );\r
+      return STATUS_ERROR;\r
+    }\r
+  }\r
+\r
+  StringIndex = STRING_ID_INVALID;\r
+  if (StringDBAddStringIdentifier (StringName, &StringIndex, Flags) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  StringIdentifier = StringDBFindStringIdentifierByName (StringName);\r
+  //\r
+  // Add this string to the end of the strings for this language.\r
+  //\r
+  Str = (STRING_LIST *) malloc (sizeof (STRING_LIST));\r
+  if (Str == NULL) {\r
+    Error (NULL, 0, 0, NULL, "memory allocation error");\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  memset ((char *) Str, 0, sizeof (STRING_LIST));\r
+  Size              = (wcslen (String) + 1) * sizeof (WCHAR);\r
+  Str->Flags        = Flags;\r
+  Str->Scope        = Scope;\r
+  Str->StringName   = StringIdentifier->StringName;\r
+  Str->LanguageName = DuplicateString (LanguageName);\r
+  Str->Str          = (WCHAR *) MALLOC (Size);\r
+  if (Str->Str == NULL) {\r
+    Error (NULL, 0, 0, NULL, "memory allocation error");\r
+    return STATUS_ERROR;\r
+  }\r
+  //\r
+  // If not formatting, just copy the string.\r
+  //\r
+  wcscpy (Str->Str, String);\r
+  if (Format) {\r
+    StringDBFormatString (Str->Str);\r
+  }\r
+  //\r
+  // Size may change after formatting. We set the size to\r
+  // the actual size of the string, including the null for\r
+  // easier processing later.\r
+  //\r
+  Str->Size = (wcslen (Str->Str) + 1) * sizeof (WCHAR);\r
+  if (Lang->String == NULL) {\r
+    Lang->String = Str;\r
+  } else {\r
+    Lang->LastString->Next = Str;\r
+  }\r
+\r
+  Lang->LastString = Str;\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+/*****************************************************************************/\r
+\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  Given a language name, see if a language list for it has been defined\r
+\r
+Arguments:\r
+\r
+  LanguageName    - like "eng"\r
+\r
+Returns:\r
+\r
+  A pointer to the language list\r
+\r
+--*/\r
+static\r
+LANGUAGE_LIST *\r
+StringDBFindLanguageList (\r
+  WCHAR *LanguageName\r
+  )\r
+{\r
+  LANGUAGE_LIST *Lang;\r
+\r
+  Lang = mDBData.LanguageList;\r
+  while (Lang != NULL) {\r
+    if (wcscmp (LanguageName, Lang->LanguageName) == 0) {\r
+      break;\r
+    }\r
+\r
+    Lang = Lang->Next;\r
+  }\r
+\r
+  return Lang;\r
+}\r
+\r
+/*****************************************************************************/\r
+STATUS\r
+StringDBSetCurrentLanguage (\r
+  WCHAR *LanguageName\r
+  )\r
+{\r
+  LANGUAGE_LIST *Lang;\r
+\r
+  Lang = StringDBFindLanguageList (LanguageName);\r
+  if (Lang == NULL) {\r
+    ParserError (0, "language not previously defined", "%S", LanguageName);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  mDBData.CurrentLanguage = Lang;\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+/*****************************************************************************/\r
+STATUS\r
+StringDBAddLanguage (\r
+  WCHAR *LanguageName,\r
+  WCHAR *PrintableLanguageName\r
+  )\r
+{\r
+  LANGUAGE_LIST *Lang;\r
+  //\r
+  // Check for redefinitions\r
+  //\r
+  Lang = StringDBFindLanguageList (LanguageName);\r
+  if (Lang != NULL) {\r
+    //\r
+    // Better be the same printable name\r
+    //\r
+    if (wcscmp (PrintableLanguageName, Lang->PrintableLanguageName) != 0) {\r
+      ParserError (\r
+        0,\r
+        "language redefinition",\r
+        "%S:%S != %S:%S",\r
+        Lang->LanguageName,\r
+        Lang->PrintableLanguageName,\r
+        LanguageName,\r
+        PrintableLanguageName\r
+        );\r
+      return STATUS_ERROR;\r
+      //\r
+      //    } else {\r
+      //      ParserWarning (0, "benign language redefinition", "%S", PrintableLanguageName);\r
+      //      return STATUS_WARNING;\r
+      //\r
+    }\r
+  } else {\r
+    //\r
+    // Allocate memory to keep track of this new language\r
+    //\r
+    Lang = (LANGUAGE_LIST *) malloc (sizeof (LANGUAGE_LIST));\r
+    if (Lang == NULL) {\r
+      Error (NULL, 0, 0, NULL, "memory allocation error");\r
+      return STATUS_ERROR;\r
+    }\r
+\r
+    memset ((char *) Lang, 0, sizeof (LANGUAGE_LIST));\r
+    //\r
+    // Save the language name, then allocate memory to save the\r
+    // printable language name\r
+    //\r
+    Lang->LanguageName[3] = 0;\r
+    wcsncpy (Lang->LanguageName, LanguageName, 3);\r
+    Lang->PrintableLanguageName = (WCHAR *) malloc ((wcslen (PrintableLanguageName) + 1) * sizeof (WCHAR));\r
+    if (Lang->PrintableLanguageName == NULL) {\r
+      Error (NULL, 0, 0, NULL, "memory allocation error");\r
+      return STATUS_ERROR;\r
+    }\r
+\r
+    wcscpy (Lang->PrintableLanguageName, PrintableLanguageName);\r
+\r
+    if (mDBData.LanguageList == NULL) {\r
+      mDBData.LanguageList = Lang;\r
+    } else {\r
+      mDBData.LastLanguageList->Next = Lang;\r
+    }\r
+\r
+    mDBData.LastLanguageList = Lang;\r
+  }\r
+  //\r
+  // Default is to make our active language this new one\r
+  //\r
+  StringDBSetCurrentLanguage (LanguageName);\r
+  //\r
+  // The first two strings for any language are the language name,\r
+  // followed by the printable language name. Add them and set them\r
+  // to referenced so they never get stripped out.\r
+  //\r
+  StringDBAddString (\r
+    LanguageName,\r
+    LANGUAGE_NAME_STRING_NAME,\r
+    NULL,\r
+    LanguageName,\r
+    FALSE,\r
+    STRING_FLAGS_REFERENCED\r
+    );\r
+  StringDBAddString (\r
+    LanguageName,\r
+    PRINTABLE_LANGUAGE_NAME_STRING_NAME,\r
+    NULL,\r
+    PrintableLanguageName,\r
+    FALSE,\r
+    STRING_FLAGS_REFERENCED\r
+    );\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+/*****************************************************************************/\r
+static\r
+STRING_IDENTIFIER *\r
+StringDBFindStringIdentifierByName (\r
+  WCHAR *StringName\r
+  )\r
+{\r
+  STRING_IDENTIFIER *Identifier;\r
+\r
+  Identifier = mDBData.StringIdentifier;\r
+  while (Identifier != NULL) {\r
+    if (wcscmp (StringName, Identifier->StringName) == 0) {\r
+      return Identifier;\r
+    }\r
+\r
+    Identifier = Identifier->Next;\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+static\r
+STRING_IDENTIFIER *\r
+StringDBFindStringIdentifierByIndex (\r
+  UINT32    StringIndex\r
+  )\r
+{\r
+  STRING_IDENTIFIER *Identifier;\r
+\r
+  Identifier = mDBData.StringIdentifier;\r
+  while (Identifier != NULL) {\r
+    if (Identifier->Index == StringIndex) {\r
+      return Identifier;\r
+    }\r
+\r
+    Identifier = Identifier->Next;\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/*****************************************************************************/\r
+static\r
+void\r
+StringDBWriteStandardFileHeader (\r
+  FILE *OutFptr\r
+  )\r
+{\r
+  UINT32  TempIndex;\r
+  for (TempIndex = 0; mSourceFileHeader[TempIndex] != NULL; TempIndex++) {\r
+    fprintf (OutFptr, "%s\n", mSourceFileHeader[TempIndex]);\r
+  }\r
+}\r
+\r
+/*****************************************************************************/\r
+\r
+/*++\r
+\r
+Routine Description:\r
+  \r
+  Given a Unicode string from an input file, reformat the string to replace\r
+  backslash control sequences with the appropriate encoding.\r
+\r
+Arguments:\r
+\r
+  String        - pointer to string to reformat\r
+\r
+Returns:\r
+\r
+  Nothing\r
+\r
+--*/\r
+void\r
+StringDBFormatString (\r
+  WCHAR   *String\r
+  )\r
+{\r
+  WCHAR *From;\r
+  WCHAR *To;\r
+  int   HexNibbles;\r
+  WCHAR HexValue;\r
+  //\r
+  // Go through the string and process any formatting characters\r
+  //\r
+  From  = String;\r
+  To    = String;\r
+  while (*From) {\r
+    if (*From == UNICODE_BACKSLASH) {\r
+      //\r
+      // First look for \wide and replace with the appropriate control character. Note that\r
+      // when you have "define STR L"ABC"", then sizeof(ABC) is 8 because the null char is\r
+      // counted. Make adjustments for this. We advance From below, so subtract 2 each time.\r
+      //\r
+      if (wcsncmp (From, UNICODE_WIDE_STRING, sizeof (UNICODE_WIDE_STRING) / sizeof (WCHAR) - 1) == 0) {\r
+        *To = WIDE_CHAR;\r
+        From += sizeof (UNICODE_WIDE_STRING) / sizeof (WCHAR) - 2;\r
+      } else if (wcsncmp (From, UNICODE_NARROW_STRING, sizeof (UNICODE_NARROW_STRING) / sizeof (WCHAR) - 1) == 0) {\r
+        //\r
+        // Found: \narrow\r
+        //\r
+        *To = NARROW_CHAR;\r
+        From += sizeof (UNICODE_NARROW_STRING) / sizeof (WCHAR) - 2;\r
+      } else if (wcsncmp (From, UNICODE_NBR_STRING, sizeof (UNICODE_NBR_STRING) / sizeof (WCHAR) - 1) == 0) {\r
+        //\r
+        // Found: \nbr\r
+        //\r
+        *To = NON_BREAKING_CHAR;\r
+        From += sizeof (UNICODE_NBR_STRING) / sizeof (WCHAR) - 2;\r
+      } else if (wcsncmp (From, UNICODE_BR_STRING, sizeof (UNICODE_BR_STRING) / sizeof (WCHAR) - 1) == 0) {\r
+        //\r
+        // Found: \br -- pass through untouched\r
+        //\r
+        *To = *From;\r
+      } else {\r
+        //\r
+        // Standard one-character control sequences such as \n, \r, \\, or \x\r
+        //\r
+        From++;\r
+        switch (*From) {\r
+        case ASCII_TO_UNICODE ('n'):\r
+          *To = UNICODE_CR;\r
+          To++;\r
+          *To = UNICODE_LF;\r
+          break;\r
+\r
+        //\r
+        // carriage return\r
+        //\r
+        case ASCII_TO_UNICODE ('r'):\r
+          *To = UNICODE_CR;\r
+          break;\r
+\r
+        //\r
+        // backslash\r
+        //\r
+        case UNICODE_BACKSLASH:\r
+          *To = UNICODE_BACKSLASH;\r
+          break;\r
+\r
+        //\r
+        // Tab\r
+        //\r
+        case ASCII_TO_UNICODE ('t'):\r
+          *To = UNICODE_TAB;\r
+          break;\r
+\r
+        //\r
+        // embedded double-quote\r
+        //\r
+        case UNICODE_DOUBLE_QUOTE:\r
+          *To = UNICODE_DOUBLE_QUOTE;\r
+          break;\r
+\r
+        //\r
+        // Hex Unicode character \x1234. We'll process up to 4 hex characters\r
+        //\r
+        case ASCII_TO_UNICODE ('x'):\r
+          HexValue = 0;\r
+          for (HexNibbles = 0; HexNibbles < 4; HexNibbles++) {\r
+            if ((From[1] >= UNICODE_0) && (From[1] <= UNICODE_9)) {\r
+              HexValue = (HexValue << 4) | (From[1] - UNICODE_0);\r
+            } else if ((From[1] >= UNICODE_a) && (From[1] <= UNICODE_f)) {\r
+              HexValue = (HexValue << 4) | (10 + From[1] - UNICODE_a);\r
+            } else if ((From[1] >= UNICODE_A) && (From[1] <= UNICODE_F)) {\r
+              HexValue = (HexValue << 4) | (10 + From[1] - UNICODE_A);\r
+            } else {\r
+              break;\r
+            }\r
+\r
+            From++;\r
+          }\r
+\r
+          if (HexNibbles == 0) {\r
+            ParserWarning (\r
+              0,\r
+              "expected at least one valid hex digit with \\x escaped character in string",\r
+              "\\%C",\r
+              *From\r
+              );\r
+          } else {\r
+            *To = HexValue;\r
+          }\r
+          break;\r
+\r
+        default:\r
+          *To = UNICODE_SPACE;\r
+          ParserWarning (0, "invalid escaped character in string", "\\%C", *From);\r
+          break;\r
+        }\r
+      }\r
+    } else {\r
+      *To = *From;\r
+    }\r
+\r
+    From++;\r
+    To++;\r
+  }\r
+\r
+  *To = 0;\r
+}\r
+\r
+/*****************************************************************************/\r
+STATUS\r
+StringDBReadDatabase (\r
+  INT8    *DBFileName,\r
+  BOOLEAN IgnoreIfNotExist,\r
+  BOOLEAN Verbose\r
+  )\r
+{\r
+  STRING_DB_HEADER    DbHeader;\r
+  STATUS              Status;\r
+  FILE                *DBFptr;\r
+  DB_DATA_ITEM_HEADER DataItemHeader;\r
+\r
+  Status  = STATUS_SUCCESS;\r
+  DBFptr  = NULL;\r
+  //\r
+  //  if (Verbose) {\r
+  //    fprintf (stdout, "Reading database file %s\n", DBFileName);\r
+  //  }\r
+  //\r
+  // Try to open the input file\r
+  //\r
+  if ((DBFptr = fopen (DBFileName, "rb")) == NULL) {\r
+    if (IgnoreIfNotExist) {\r
+      return STATUS_SUCCESS;\r
+    }\r
+\r
+    Error (NULL, 0, 0, DBFileName, "failed to open input database file for reading");\r
+    return STATUS_ERROR;\r
+  }\r
+  //\r
+  // Read and verify the database header\r
+  //\r
+  if (fread ((void *) &DbHeader, sizeof (STRING_DB_HEADER), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, DBFileName, "failed to read header from database file");\r
+    Status = STATUS_ERROR;\r
+    goto Finish;\r
+  }\r
+\r
+  if (DbHeader.Key != STRING_DB_KEY) {\r
+    Error (NULL, 0, 0, DBFileName, "invalid header in database file");\r
+    Status = STATUS_ERROR;\r
+    goto Finish;\r
+  }\r
+\r
+  if ((DbHeader.Version & STRING_DB_MAJOR_VERSION_MASK) != (STRING_DB_VERSION & STRING_DB_MAJOR_VERSION_MASK)) {\r
+    Error (NULL, 0, 0, DBFileName, "incompatible database file version -- rebuild clean");\r
+    Status = STATUS_ERROR;\r
+    goto Finish;\r
+  }\r
+  //\r
+  // Read remaining items\r
+  //\r
+  while (fread (&DataItemHeader, sizeof (DataItemHeader), 1, DBFptr) == 1) {\r
+    switch (DataItemHeader.DataType) {\r
+    case DB_DATA_TYPE_STRING_IDENTIFIER:\r
+      StringDBReadStringIdentifier (DBFptr);\r
+      break;\r
+\r
+    case DB_DATA_TYPE_LANGUAGE_DEFINITION:\r
+      StringDBReadLanguageDefinition (DBFptr);\r
+      break;\r
+\r
+    case DB_DATA_TYPE_STRING_DEFINITION:\r
+      StringDBReadString (DBFptr);\r
+      break;\r
+\r
+    default:\r
+      Error (\r
+        NULL,\r
+        0,\r
+        0,\r
+        "database corrupted",\r
+        "invalid data item type 0x%X at offset 0x%X",\r
+        (UINT32) DataItemHeader.DataType,\r
+        ftell (DBFptr) - sizeof (DataItemHeader)\r
+        );\r
+      Status = STATUS_ERROR;\r
+      goto Finish;\r
+    }\r
+  }\r
+\r
+Finish:\r
+  if (DBFptr != NULL) {\r
+    fclose (DBFptr);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/*****************************************************************************/\r
+\r
+/*++\r
+\r
+Routine Description:\r
+  \r
+  Write everything we know to the output database file. Write:\r
+\r
+  Database header\r
+  String identifiers[]\r
+  StringPacks[]\r
+\r
+Arguments:\r
+\r
+  DBFileName    - name of the file to write to\r
+  Verbose       - for debug purposes, print info messages along the way.\r
+\r
+Returns:\r
+\r
+  STATUS\r
+\r
+--*/\r
+STATUS\r
+StringDBWriteDatabase (\r
+  INT8    *DBFileName,\r
+  BOOLEAN Verbose\r
+  )\r
+{\r
+  STRING_DB_HEADER  DbHeader;\r
+  UINT32            Counter;\r
+  UINT32            StrLen;\r
+  LANGUAGE_LIST     *Lang;\r
+  STRING_IDENTIFIER *StringIdentifier;\r
+  STRING_LIST       *StrList;\r
+  FILE              *DBFptr;\r
+\r
+  if (Verbose) {\r
+    fprintf (stdout, "Writing database %s\n", DBFileName);\r
+  }\r
+\r
+  if ((DBFptr = fopen (DBFileName, "wb")) == NULL) {\r
+    Error (NULL, 0, 0, DBFileName, "failed to open output database file for writing");\r
+    return STATUS_ERROR;\r
+  }\r
+  //\r
+  // Fill in and write the database header\r
+  //\r
+  memset (&DbHeader, 0, sizeof (STRING_DB_HEADER));\r
+  DbHeader.HeaderSize = sizeof (STRING_DB_HEADER);\r
+  DbHeader.Key        = STRING_DB_KEY;\r
+  DbHeader.Version    = STRING_DB_VERSION;\r
+  //\r
+  // Count the number of languages we have\r
+  //\r
+  for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {\r
+    DbHeader.NumLanguages++;\r
+  }\r
+  //\r
+  // Count up how many string identifiers we have, and total up the\r
+  // size of the names plus the size of the flags field we will\r
+  // write out too.\r
+  //\r
+  DbHeader.NumStringIdenfiers = mDBData.NumStringIdentifiers;\r
+  StringIdentifier            = mDBData.StringIdentifier;\r
+  for (Counter = 0; Counter < mDBData.NumStringIdentifiers; Counter++) {\r
+    StrLen = wcslen (StringIdentifier->StringName) + 1;\r
+    DbHeader.StringIdentifiersSize += StrLen * sizeof (WCHAR) + sizeof (StringIdentifier->Flags);\r
+    StringIdentifier = StringIdentifier->Next;\r
+  }\r
+\r
+  //\r
+  // Write the header\r
+  //\r
+  fwrite (&DbHeader, sizeof (STRING_DB_HEADER), 1, DBFptr);\r
+  if (Verbose) {\r
+    fprintf (stdout, "  Number of string identifiers  0x%04X\n", DbHeader.NumStringIdenfiers);\r
+    fprintf (stdout, "  Number of languages           %d\n", DbHeader.NumLanguages);\r
+  }\r
+  //\r
+  // Write the string identifiers\r
+  //\r
+  for (StringIdentifier = mDBData.StringIdentifier; StringIdentifier != NULL; StringIdentifier = StringIdentifier->Next) {\r
+    StringDBWriteStringIdentifier (\r
+      DBFptr,\r
+      (UINT16) StringIdentifier->Index,\r
+      StringIdentifier->Flags,\r
+      StringIdentifier->StringName\r
+      );\r
+  }\r
+  //\r
+  // Now write all the strings for each language\r
+  //\r
+  for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {\r
+    StringDBWriteLanguageDefinition (DBFptr, Lang->LanguageName, Lang->PrintableLanguageName);\r
+    for (StrList = Lang->String; StrList != NULL; StrList = StrList->Next) {\r
+      StringDBWriteString (\r
+        DBFptr,\r
+        StrList->Flags,\r
+        Lang->LanguageName,\r
+        StrList->StringName,\r
+        StrList->Scope,\r
+        StrList->Str\r
+        );\r
+    }\r
+  }\r
+\r
+  fclose (DBFptr);\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+STATUS\r
+StringDBSetStringReferenced (\r
+  INT8      *StringIdentifierName,\r
+  BOOLEAN   IgnoreNotFound\r
+  )\r
+{\r
+  STRING_IDENTIFIER *Id;\r
+  WCHAR             *WName;\r
+  STATUS            Status;\r
+  //\r
+  // See if it's already been defined.\r
+  //\r
+  Status  = STATUS_SUCCESS;\r
+  WName   = (WCHAR *) malloc ((strlen (StringIdentifierName) + 1) * sizeof (WCHAR));\r
+#ifdef USE_VC8\r
+  swprintf (WName, (strlen (StringIdentifierName) + 1) * sizeof (WCHAR), L"%S", StringIdentifierName);\r
+#else\r
+  swprintf (WName, L"%S", StringIdentifierName);\r
+#endif\r
+  Id = StringDBFindStringIdentifierByName (WName);\r
+  if (Id != NULL) {\r
+    Id->Flags |= STRING_FLAGS_REFERENCED;\r
+  } else {\r
+    if (IgnoreNotFound == 0) {\r
+      ParserWarning (0, StringIdentifierName, "string identifier not found in database");\r
+      Status = STATUS_WARNING;\r
+    }\r
+  }\r
+\r
+  free (WName);\r
+  return Status;\r
+}\r
+\r
+/*****************************************************************************/\r
+\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  Dump the contents of a database to an output unicode file.\r
+\r
+Arguments:\r
+\r
+  DBFileName        - name of the pre-existing database file to read\r
+  OutputFileName    - name of the file to dump the database contents to\r
+  Verbose           - for printing of additional info useful for debugging\r
+\r
+Returns:\r
+\r
+  STATUS\r
+\r
+Notes:\r
+\r
+  There's some issue with the unicode printing routines. Therefore to \r
+  write to the output file properly, open it as binary and use fwrite.\r
+  Ideally we could open it with just L"w" and use fwprintf().\r
+\r
+--*/\r
+STATUS\r
+StringDBDumpDatabase (\r
+  INT8                *DBFileName,\r
+  INT8                *OutputFileName,\r
+  BOOLEAN             Verbose\r
+  )\r
+{\r
+  LANGUAGE_LIST     *Lang;\r
+  STRING_IDENTIFIER *StringIdentifier;\r
+  STRING_LIST       *StrList;\r
+  FILE              *OutFptr;\r
+  WCHAR             WChar;\r
+  WCHAR             *WOutputFileName;\r
+  WCHAR             CrLf[2];\r
+  WCHAR             Line[200];\r
+  WCHAR             *Scope;\r
+  //\r
+  // This function assumes the database has already been read, and\r
+  // we're just dumping our internal data structures to a unicode file.\r
+  //\r
+  if (Verbose) {\r
+    fprintf (stdout, "Dumping database file %s\n", DBFileName);\r
+  }\r
+\r
+  WOutputFileName = AsciiToWchar (OutputFileName);\r
+  OutFptr         = _wfopen (WOutputFileName, L"wb");\r
+  free (WOutputFileName);\r
+  if (OutFptr == NULL) {\r
+    Error (NULL, 0, 0, OutputFileName, "failed to open output file for writing");\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  WChar = UNICODE_FILE_START;\r
+  fwrite (&WChar, sizeof (WCHAR), 1, OutFptr);\r
+  CrLf[1] = UNICODE_LF;\r
+  CrLf[0] = UNICODE_CR;\r
+  //\r
+  // The default control character is '/'. Make it '#' by writing\r
+  // "/=#" to the output file.\r
+  //\r
+#ifdef USE_VC8\r
+  swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"/=#");\r
+#else\r
+  swprintf (Line, L"/=#");\r
+#endif\r
+  fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);\r
+  fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);\r
+  fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);\r
+  //\r
+  // Dump all the string identifiers and their values\r
+  //\r
+  StringDBAssignStringIndexes ();\r
+  for (StringIdentifier = mDBData.StringIdentifier; StringIdentifier != NULL; StringIdentifier = StringIdentifier->Next) {\r
+    //\r
+    // Write the "#define " string\r
+    //\r
+    if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {\r
+#ifdef USE_VC8\r
+      swprintf (\r
+        Line,\r
+        wcslen(Line) * sizeof (WCHAR),\r
+        L"%s %-60.60s 0x%04X",\r
+        DEFINE_STR,\r
+        StringIdentifier->StringName,\r
+        StringIdentifier->Index\r
+        );\r
+#else\r
+      swprintf (\r
+        Line,\r
+        L"%s %-60.60s 0x%04X",\r
+        DEFINE_STR,\r
+        StringIdentifier->StringName,\r
+        StringIdentifier->Index\r
+        );\r
+#endif\r
+    } else {\r
+#ifdef USE_VC8\r
+      swprintf (\r
+        Line,\r
+        wcslen(Line) * sizeof (WCHAR), \r
+        L"%s %-60.60s 0x%04X  // NOT REFERENCED",\r
+        DEFINE_STR,\r
+        StringIdentifier->StringName,\r
+        StringIdentifier->Index\r
+        );\r
+#else\r
+      swprintf (\r
+        Line,\r
+        L"%s %-60.60s 0x%04X  // NOT REFERENCED",\r
+        DEFINE_STR,\r
+        StringIdentifier->StringName,\r
+        StringIdentifier->Index\r
+        );\r
+#endif\r
+    }\r
+\r
+    fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);\r
+    fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);\r
+  }\r
+\r
+  fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);\r
+  //\r
+  // Now write all the strings for each language.\r
+  //\r
+  WChar = UNICODE_DOUBLE_QUOTE;\r
+  Scope = NULL;\r
+  for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {\r
+    fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);\r
+#ifdef USE_VC8\r
+    swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"#langdef %s \"%s\"", Lang->LanguageName, Lang->PrintableLanguageName);\r
+#else\r
+    swprintf (Line, L"#langdef %s \"%s\"", Lang->LanguageName, Lang->PrintableLanguageName);\r
+#endif\r
+    fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);\r
+    fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);\r
+    fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);\r
+    //\r
+    // Now the strings (in double-quotes) for this language. Write\r
+    // #string STR_NAME  #language eng "string"\r
+    //\r
+    for (StrList = Lang->String; StrList != NULL; StrList = StrList->Next) {\r
+      //\r
+      // Print the internal flags for debug\r
+      //\r
+#ifdef USE_VC8\r
+      swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"// flags=0x%02X", (UINT32) StrList->Flags);\r
+#else\r
+      swprintf (Line, L"// flags=0x%02X", (UINT32) StrList->Flags);\r
+#endif\r
+      fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);\r
+      fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);\r
+      //\r
+      // Print the scope if changed\r
+      //\r
+      if ((Scope == NULL) || (wcscmp (Scope, StrList->Scope) != 0)) {\r
+#ifdef USE_VC8\r
+        swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"#scope %s", StrList->Scope);\r
+#else\r
+        swprintf (Line, L"#scope %s", StrList->Scope);\r
+#endif\r
+        fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);\r
+        fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);\r
+        Scope = StrList->Scope;\r
+      }\r
+\r
+#ifdef USE_VC8\r
+      swprintf (\r
+        Line,\r
+        wcslen(Line) * sizeof (WCHAR), \r
+        L"#string %-50.50s #language %s \"",\r
+        StrList->StringName,\r
+        Lang->LanguageName\r
+        );\r
+#else\r
+      swprintf (\r
+        Line,\r
+        L"#string %-50.50s #language %s \"",\r
+        StrList->StringName,\r
+        Lang->LanguageName\r
+        );\r
+#endif\r
+      fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);\r
+      fwrite (StrList->Str, StrList->Size - sizeof (WCHAR), 1, OutFptr);\r
+#ifdef USE_VC8\r
+      swprintf (Line, wcslen(Line) * sizeof (WCHAR), L"\"");\r
+#else\r
+      swprintf (Line, L"\"");\r
+#endif\r
+      fwrite (Line, wcslen (Line) * sizeof (WCHAR), 1, OutFptr);\r
+      fwrite (&CrLf, sizeof (CrLf), 1, OutFptr);\r
+    }\r
+  }\r
+\r
+  fclose (OutFptr);\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+/*****************************************************************************/\r
+\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  Given a primary language, a string identifier number, and a list of\r
+  languages, find a secondary string.\r
+\r
+Arguments:\r
+\r
+  LanguageName      - primary language, like "spa"\r
+  StringId          - string index value\r
+  LanguageList      - linked list of "eng", "spa+cat",...\r
+\r
+Returns:\r
+\r
+  Pointer to a secondary string if found. NULL otherwise.\r
+\r
+Notes:\r
\r
+  Given: LanguageName "spa"   and  LanguageList "spa+cat", match the\r
+  "spa" and extract the "cat" and see if there is a string defined\r
+  for "cat".StringId.\r
+\r
+--*/\r
+static\r
+STATUS\r
+StringDBWriteStringIdentifier (\r
+  FILE                *DBFptr,\r
+  UINT16              StringId,\r
+  UINT16              Flags,\r
+  WCHAR               *IdentifierName\r
+  )\r
+{\r
+  DB_DATA_ITEM_HEADER Hdr;\r
+  memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER));\r
+  Hdr.DataType = DB_DATA_TYPE_STRING_IDENTIFIER;\r
+  if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, "failed to write string to output database file", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (fwrite (&StringId, sizeof (StringId), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, "failed to write StringId to output database", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (fwrite (&Flags, sizeof (Flags), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, "failed to write StringId flags to output database", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (StringDBWriteGenericString (DBFptr, IdentifierName) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+static\r
+STATUS\r
+StringDBReadStringIdentifier (\r
+  FILE                *DBFptr\r
+  )\r
+{\r
+  WCHAR   *IdentifierName;\r
+  UINT16  Flags;\r
+  UINT16  StringId;\r
+  UINT16  Size;\r
+\r
+  if (fread (&StringId, sizeof (StringId), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, "failed to read StringId from database", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (fread (&Flags, sizeof (Flags), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, "failed to read StringId flags from database", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (StringDBReadGenericString (DBFptr, &Size, &IdentifierName) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  StringDBAddStringIdentifier (IdentifierName, &StringId, Flags);\r
+  //\r
+  // printf ("STRID:  0x%04X %S\n", (UINT32)StringId, IdentifierName);\r
+  //\r
+  FREE (IdentifierName);\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+static\r
+STATUS\r
+StringDBWriteString (\r
+  FILE            *DBFptr,\r
+  UINT16          Flags,\r
+  WCHAR           *Language,\r
+  WCHAR           *StringName,\r
+  WCHAR           *Scope,\r
+  WCHAR           *Str\r
+  )\r
+{\r
+  DB_DATA_ITEM_HEADER Hdr;\r
+  memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER));\r
+  Hdr.DataType = DB_DATA_TYPE_STRING_DEFINITION;\r
+  if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, "failed to write string header to output database file", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (fwrite (&Flags, sizeof (Flags), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, "failed to write string flags to output database", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (StringDBWriteGenericString (DBFptr, Language) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (StringDBWriteGenericString (DBFptr, StringName) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (StringDBWriteGenericString (DBFptr, Scope) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (StringDBWriteGenericString (DBFptr, Str) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+  //\r
+  // printf ("DBWriteString: %S.%S.%S\n", Language, StringName, Scope);\r
+  //\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+static\r
+STATUS\r
+StringDBReadString (\r
+  FILE            *DBFptr\r
+  )\r
+{\r
+  UINT16  Flags;\r
+  UINT16  Size;\r
+  WCHAR   *Language;\r
+  WCHAR   *StringName;\r
+  WCHAR   *Scope;\r
+  WCHAR   *Str;\r
+\r
+  if (fread (&Flags, sizeof (Flags), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, "failed to read string flags from database", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (StringDBReadGenericString (DBFptr, &Size, &Language) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (StringDBReadGenericString (DBFptr, &Size, &StringName) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (StringDBReadGenericString (DBFptr, &Size, &Scope) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (StringDBReadGenericString (DBFptr, &Size, &Str) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+  //\r
+  // If the first or second string (language name and printable language name),\r
+  // then skip them. They're added via language definitions data items in\r
+  // the database.\r
+  //\r
+  if (StringName[0] != L'$') {\r
+    StringDBAddString (Language, StringName, Scope, Str, FALSE, Flags);\r
+  }\r
+  //\r
+  // printf ("DBReadString: %S.%S.%S\n", Language, StringName, Scope);\r
+  //\r
+  FREE (Language);\r
+  FREE (StringName);\r
+  if (Str != NULL) {\r
+    FREE (Str);\r
+  }\r
+\r
+  if (Scope != NULL) {\r
+    FREE (Scope);\r
+  }\r
+\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+static\r
+STATUS\r
+StringDBWriteLanguageDefinition (\r
+  FILE            *DBFptr,\r
+  WCHAR           *LanguageName,\r
+  WCHAR           *PrintableLanguageName\r
+  )\r
+{\r
+  DB_DATA_ITEM_HEADER Hdr;\r
+  memset (&Hdr, 0, sizeof (DB_DATA_ITEM_HEADER));\r
+  Hdr.DataType = DB_DATA_TYPE_LANGUAGE_DEFINITION;\r
+  if (fwrite (&Hdr, sizeof (DB_DATA_ITEM_HEADER), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, "failed to write string to output database file", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (StringDBWriteGenericString (DBFptr, LanguageName) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (StringDBWriteGenericString (DBFptr, PrintableLanguageName) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+static\r
+STATUS\r
+StringDBReadLanguageDefinition (\r
+  FILE            *DBFptr\r
+  )\r
+{\r
+  WCHAR   *LanguageName;\r
+  WCHAR   *PrintableLanguageName;\r
+  UINT16  Size;\r
+  STATUS  Status;\r
+\r
+  if (StringDBReadGenericString (DBFptr, &Size, &LanguageName) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (StringDBReadGenericString (DBFptr, &Size, &PrintableLanguageName) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+  //\r
+  // printf("LANG: %S %S\n", LanguageName, PrintableLanguageName);\r
+  //\r
+  Status = StringDBAddLanguage (LanguageName, PrintableLanguageName);\r
+  FREE (LanguageName);\r
+  FREE (PrintableLanguageName);\r
+  return Status;\r
+}\r
+//\r
+// All unicode strings in the database consist of a UINT16 length\r
+// field, followed by the string itself. This routine reads one\r
+// of those and returns the info.\r
+//\r
+static\r
+STATUS\r
+StringDBReadGenericString (\r
+  FILE      *DBFptr,\r
+  UINT16    *Size,\r
+  WCHAR     **Str\r
+  )\r
+{\r
+  UINT16  LSize;\r
+  UINT16  Flags;\r
+  WCHAR   *LStr;\r
+\r
+  if (fread (&LSize, sizeof (UINT16), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, "failed to read a string length field from the database", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (fread (&Flags, sizeof (UINT16), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, "failed to read a string flags field from the database", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  LStr = MALLOC (LSize);\r
+  if (LStr == NULL) {\r
+    Error (__FILE__, __LINE__, 0, "memory allocation failed reading the database", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (fread (LStr, sizeof (WCHAR), (UINT32) LSize / sizeof (WCHAR), DBFptr) != (UINT32) LSize / sizeof (WCHAR)) {\r
+    Error (NULL, 0, 0, "failed to read string from database", NULL);\r
+    Error (NULL, 0, 0, "database read failure", "offset 0x%X", ftell (DBFptr));\r
+    free (LStr);\r
+    return STATUS_ERROR;\r
+  }\r
+  //\r
+  // printf ("DBR: %S\n", LStr);\r
+  //\r
+  // If the flags field indicated we were asked to write a NULL string, then\r
+  // return them a NULL pointer.\r
+  //\r
+  if (Flags & STRING_FLAGS_UNDEFINED) {\r
+    *Size = 0;\r
+    *Str  = NULL;\r
+  } else {\r
+    *Size = LSize;\r
+    *Str  = LStr;\r
+  }\r
+\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+static\r
+STATUS\r
+StringDBWriteGenericString (\r
+  FILE      *DBFptr,\r
+  WCHAR     *Str\r
+  )\r
+{\r
+  UINT16  Size;\r
+  UINT16  Flags;\r
+  WCHAR   ZeroString[1];\r
+  //\r
+  // Strings in the database consist of a size UINT16 followed\r
+  // by the string itself.\r
+  //\r
+  if (Str == NULL) {\r
+    ZeroString[0] = 0;\r
+    Str           = ZeroString;\r
+    Size          = sizeof (ZeroString);\r
+    Flags         = STRING_FLAGS_UNDEFINED;\r
+  } else {\r
+    Flags = 0;\r
+    Size  = (UINT16) ((wcslen (Str) + 1) * sizeof (WCHAR));\r
+  }\r
+\r
+  if (fwrite (&Size, sizeof (UINT16), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, "failed to write string size to database", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (fwrite (&Flags, sizeof (UINT16), 1, DBFptr) != 1) {\r
+    Error (NULL, 0, 0, "failed to write string flags to database", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (fwrite (Str, sizeof (WCHAR), Size / sizeof (WCHAR), DBFptr) != Size / sizeof (WCHAR)) {\r
+    Error (NULL, 0, 0, "failed to write string to database", NULL);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+static\r
+STRING_LIST *\r
+StringDBFindString (\r
+  WCHAR                       *LanguageName,\r
+  WCHAR                       *StringName,\r
+  WCHAR                       *Scope,\r
+  WCHAR_STRING_LIST           *LanguagesOfInterest,\r
+  WCHAR_MATCHING_STRING_LIST  *IndirectionList\r
+  )\r
+{\r
+  LANGUAGE_LIST               *Lang;\r
+  STRING_LIST                 *CurrString;\r
+  WCHAR_MATCHING_STRING_LIST  *IndListPtr;\r
+  WCHAR                       TempLangName[LANGUAGE_IDENTIFIER_NAME_LEN + 1];\r
+  WCHAR                       *WCharPtr;\r
+\r
+  //\r
+  // If we were given an indirection list, then see if one was specified for this\r
+  // string identifier. That is to say, if the indirection says "STR_ID_MY_FAVORITE MyScope",\r
+  // then if this string name matches one in the list, then do a lookup with the\r
+  // specified scope and return that value.\r
+  //\r
+  if (IndirectionList != NULL) {\r
+    for (IndListPtr = IndirectionList; IndListPtr != NULL; IndListPtr = IndListPtr->Next) {\r
+      if (wcscmp (StringName, IndListPtr->Str1) == 0) {\r
+        CurrString = StringDBFindString (LanguageName, StringName, IndListPtr->Str2, LanguagesOfInterest, NULL);\r
+        if (CurrString != NULL) {\r
+          return CurrString;\r
+        }\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // First look for exact match language.stringname\r
+  //\r
+  for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {\r
+    if (wcscmp (LanguageName, Lang->LanguageName) == 0) {\r
+      //\r
+      // Found language match. Try to find string name match\r
+      //\r
+      for (CurrString = Lang->String; CurrString != NULL; CurrString = CurrString->Next) {\r
+        if (wcscmp (StringName, CurrString->StringName) == 0) {\r
+          //\r
+          // Found a string name match. See if we're supposed to find\r
+          // a scope match.\r
+          //\r
+          if (Scope != NULL) {\r
+            if (wcscmp (CurrString->Scope, Scope) == 0) {\r
+              return CurrString;\r
+            }\r
+          } else {\r
+            return CurrString;\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+  //\r
+  // If we got here, then we didn't find a match. Look for secondary string\r
+  // matches. That is to say, if we're processing "spa", and they requested\r
+  // "spa+cat", then recursively call with "cat"\r
+  //\r
+  while (LanguagesOfInterest != NULL) {\r
+    //\r
+    // If this is the language we're looking for, then process the\r
+    // languages of interest list for it.\r
+    //\r
+    if (wcsncmp (LanguageName, LanguagesOfInterest->Str, LANGUAGE_IDENTIFIER_NAME_LEN) == 0) {\r
+      WCharPtr = LanguagesOfInterest->Str + LANGUAGE_IDENTIFIER_NAME_LEN;\r
+      while (*WCharPtr) {\r
+        //\r
+        // Double-check the length, though it should have been checked on the\r
+        // command line.\r
+        //\r
+        if (wcslen (WCharPtr) < LANGUAGE_IDENTIFIER_NAME_LEN) {\r
+          Error (NULL, 0, 0, "malformed alternate language list", "%S", LanguagesOfInterest->Str);\r
+          return NULL;\r
+        }\r
+\r
+        wcsncpy (TempLangName, WCharPtr, LANGUAGE_IDENTIFIER_NAME_LEN);\r
+        TempLangName[LANGUAGE_IDENTIFIER_NAME_LEN]  = 0;\r
+        CurrString = StringDBFindString (TempLangName, StringName, NULL, NULL, IndirectionList);\r
+        if (CurrString != NULL) {\r
+          return CurrString;\r
+        }\r
+\r
+        WCharPtr += LANGUAGE_IDENTIFIER_NAME_LEN;\r
+      }\r
+    }\r
+\r
+    LanguagesOfInterest = LanguagesOfInterest->Next;\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+STATUS\r
+StringDBSetScope (\r
+  WCHAR   *Scope\r
+  )\r
+{\r
+  //\r
+  // Free up existing scope memory.\r
+  //\r
+  if (mDBData.CurrentScope != NULL) {\r
+    FREE (mDBData.CurrentScope);\r
+  }\r
+\r
+  mDBData.CurrentScope = DuplicateString (Scope);\r
+  return STATUS_SUCCESS;\r
+}\r
+//\r
+// We typically don't assign index values to string identifiers\r
+// until we're ready to write out files. To reduce the size of\r
+// the output file, re-order the string identifiers to move any\r
+// unreferenced ones to the end. Then we'll walk the list\r
+// again to assign string indexes, keeping track of the last\r
+// one referenced.\r
+//\r
+static\r
+void\r
+StringDBAssignStringIndexes (\r
+  VOID\r
+  )\r
+{\r
+  STRING_IDENTIFIER *StrId;\r
+  STRING_IDENTIFIER *FirstUsed;\r
+  STRING_IDENTIFIER *LastUsed;\r
+  STRING_IDENTIFIER *FirstUnused;\r
+  STRING_IDENTIFIER *LastUnused;\r
+  UINT32            Index;\r
+  UINT32            MaxReferenced;\r
+\r
+  //\r
+  // Create two lists -- used and unused. Then put them together with\r
+  // the unused ones on the end.\r
+  //\r
+  FirstUsed   = NULL;\r
+  LastUsed    = NULL;\r
+  FirstUnused = NULL;\r
+  LastUnused  = NULL;\r
+  StrId       = mDBData.StringIdentifier;\r
+  while (StrId != NULL) {\r
+    if ((StrId->Flags & STRING_FLAGS_REFERENCED) == 0) {\r
+      //\r
+      // Put it on the unused list\r
+      //\r
+      if (FirstUnused == NULL) {\r
+        FirstUnused = StrId;\r
+      } else {\r
+        LastUnused->Next = StrId;\r
+      }\r
+\r
+      LastUnused        = StrId;\r
+      StrId             = StrId->Next;\r
+      LastUnused->Next  = NULL;\r
+    } else {\r
+      //\r
+      // Put it on the used list\r
+      //\r
+      if (FirstUsed == NULL) {\r
+        FirstUsed = StrId;\r
+      } else {\r
+        LastUsed->Next = StrId;\r
+      }\r
+\r
+      LastUsed        = StrId;\r
+      StrId           = StrId->Next;\r
+      LastUsed->Next  = NULL;\r
+    }\r
+  }\r
+  //\r
+  // Join the lists\r
+  //\r
+  if (FirstUsed != NULL) {\r
+    mDBData.StringIdentifier  = FirstUsed;\r
+    LastUsed->Next            = FirstUnused;\r
+  } else {\r
+    mDBData.StringIdentifier = FirstUnused;\r
+  }\r
+\r
+  MaxReferenced = 0;\r
+  Index         = 0;\r
+  for (StrId = mDBData.StringIdentifier; StrId != NULL; StrId = StrId->Next) {\r
+    StrId->Index = Index;\r
+    Index++;\r
+    if (StrId->Flags & STRING_FLAGS_REFERENCED) {\r
+      mDBData.NumStringIdentifiersReferenced = Index;\r
+    }\r
+  }\r
+\r
+  mDBData.NumStringIdentifiers = Index;\r
+}\r
+\r
+static\r
+WCHAR *\r
+DuplicateString (\r
+  WCHAR   *Str\r
+  )\r
+{\r
+  WCHAR *NewStr;\r
+  if (Str == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  NewStr = MALLOC ((wcslen (Str) + 1) * sizeof (WCHAR));\r
+  if (NewStr == NULL) {\r
+    Error (NULL, 0, 0, "memory allocation failure", NULL);\r
+    return NULL;\r
+  }\r
+\r
+  wcscpy (NewStr, Str);\r
+  return NewStr;\r
+}\r
+\r
+static\r
+WCHAR *\r
+AsciiToWchar (\r
+  INT8 *Str\r
+  )\r
+{\r
+  UINT32  Len;\r
+  WCHAR   *NewStr;\r
+  WCHAR   *Ptr;\r
+\r
+  Len     = strlen (Str) + 1;\r
+  NewStr  = (WCHAR *) malloc (Len * sizeof (WCHAR));\r
+  for (Ptr = NewStr; *Str != 0; Str++, Ptr++) {\r
+    *Ptr = (UINT16) (UINT8) *Str;\r
+  }\r
+\r
+  *Ptr = 0;\r
+  return NewStr;\r
+}\r
+\r
+/*****************************************************************************/\r
+\r
+/*++\r
+\r
+Routine Description:\r
+\r
+  Create an HII export string pack for the strings in our database.\r
+\r
+Arguments:\r
+\r
+  FileName        - name of the output file to write \r
+\r
+Returns:\r
+\r
+  STATUS\r
+\r
+\r
+--*/\r
+STATUS\r
+StringDBCreateHiiExportPack (\r
+  INT8                        *FileName\r
+  )\r
+{\r
+  FILE                        *Fptr;\r
+  LANGUAGE_LIST               *Lang;\r
+  STRING_LIST                 *CurrString;\r
+  STRING_LIST                 EmptyString;\r
+  UINT32                      Offset;\r
+  UINT32                      StringIndex;\r
+  UINT32                      TempIndex;\r
+  EFI_HII_STRING_PACK_HEADER  StringPack;\r
+  UINT32                      Len;\r
+  WCHAR                       ZeroString[1];\r
+  WCHAR                       *TempStringPtr;\r
+  WCHAR                       *LangName;\r
+  STRING_IDENTIFIER           *StringIdentifier;\r
+\r
+  if ((Fptr = fopen (FileName, "wb")) == NULL) {\r
+    Error (NULL, 0, 0, FileName, "failed to open output HII export file");\r
+    return STATUS_ERROR;\r
+  }\r
+  //\r
+  // Assign index values to the string identifiers\r
+  //\r
+  StringDBAssignStringIndexes ();\r
+  //\r
+  // If a given string is not defined, then we'll use this one.\r
+  //\r
+  memset (&EmptyString, 0, sizeof (EmptyString));\r
+  EmptyString.Size  = sizeof (ZeroString);\r
+  EmptyString.Str   = ZeroString;\r
+  //\r
+  // Process each language, then each string for each langage\r
+  //\r
+  ZeroString[0] = 0;\r
+  for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {\r
+    //\r
+    // Process each string for this language. We have to make 3 passes on the strings:\r
+    //   Pass1: computes sizes and fill in the string pack header\r
+    //   Pass2: write the array of offsets\r
+    //   Pass3: write the strings\r
+    //\r
+    //\r
+    // PASS 1: Fill in and print the HII string pack header\r
+    //\r
+    // Compute the size for this language package and write\r
+    // the header out. Each string package contains:\r
+    //   Header\r
+    //   Offset[]  -- an array of offsets to strings, of type RELOFST each\r
+    //   String[]  -- the actual strings themselves\r
+    //\r
+    memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER));\r
+    StringPack.Header.Type        = EFI_HII_STRING;\r
+    StringPack.NumStringPointers  = (UINT16) mDBData.NumStringIdentifiersReferenced;\r
+    LangName                      = Lang->LanguageName;\r
+    //\r
+    // First string is the language name. If we're printing all languages, then\r
+    // it's just the "spa". If we were given a list of languages to print, then it's\r
+    // the "spacat" string. Compute its offset and fill in\r
+    // the info in the header. Since we know the language name string's length,\r
+    // and the printable language name follows it, use that info to fill in the\r
+    // entry for the printable language name as well.\r
+    //\r
+    StringPack.LanguageNameString = (STRING_OFFSET) (sizeof (EFI_HII_STRING_PACK_HEADER) + (mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET)));\r
+    StringPack.PrintableLanguageName = (STRING_OFFSET) (StringPack.LanguageNameString + (wcslen (LangName) + 1) * sizeof (WCHAR));\r
+    //\r
+    // Add up the size of all strings so we can fill in our header.\r
+    //\r
+    Len = 0;\r
+    for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {\r
+      //\r
+      // For the first string (language name), we print out the "spacat" if they\r
+      // requested it. We set LangName to point to the proper language name string above.\r
+      //\r
+      if (StringIndex == STRING_ID_LANGUAGE_NAME) {\r
+        Len += (wcslen (LangName) + 1) * sizeof (WCHAR);\r
+      } else {\r
+        //\r
+        // Find a string with this language.stringname\r
+        //\r
+        StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);\r
+        if (StringIdentifier == NULL) {\r
+          Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);\r
+          return STATUS_ERROR;\r
+        }\r
+        //\r
+        // Find a matching string if this string identifier was referenced\r
+        //\r
+        EmptyString.Flags = STRING_FLAGS_UNDEFINED;\r
+        CurrString        = NULL;\r
+        if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {\r
+          CurrString = StringDBFindString (\r
+                        Lang->LanguageName,\r
+                        StringIdentifier->StringName,\r
+                        NULL,\r
+                        NULL, // LanguagesOfInterest,\r
+                        NULL\r
+                        );\r
+          //\r
+          // IndirectionList);\r
+          //\r
+          if (NULL == CurrString) {\r
+            //\r
+            // If string for Lang->LanguageName is not found, try to get an English version\r
+            //\r
+            CurrString = StringDBFindString (\r
+                          L"eng",\r
+                          StringIdentifier->StringName,\r
+                          NULL,\r
+                          NULL, // LanguagesOfInterest,\r
+                          NULL\r
+                          );\r
+            //\r
+            // IndirectionList);\r
+            //\r
+          }\r
+        }\r
+\r
+        if (CurrString == NULL) {\r
+          CurrString = &EmptyString;\r
+          EmptyString.Flags |= StringIdentifier->Flags;\r
+        }\r
+\r
+        Len += CurrString->Size;\r
+      }\r
+    }\r
+    StringPack.Header.Length =    sizeof (EFI_HII_STRING_PACK_HEADER) \r
+                                + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET) \r
+                                + Len;\r
+    //\r
+    // Write out the string pack header\r
+    //\r
+    fwrite ((void *) &StringPack, sizeof (StringPack), 1, Fptr);\r
+    //\r
+    // PASS2 : write the offsets\r
+    //\r
+    // Traverse the list of strings again and write the array of offsets. The\r
+    // offset to the first string is the size of the string pack header\r
+    // plus the size of the offsets array. The other strings follow it.\r
+    //\r
+    StringIndex = 0;\r
+    Offset      = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);\r
+    for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {\r
+      //\r
+      // Write the offset\r
+      //\r
+      fwrite (&Offset, sizeof (STRING_OFFSET), 1, Fptr);\r
+      //\r
+      // Find the string name\r
+      //\r
+      StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);\r
+      if (StringIdentifier == NULL) {\r
+        Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);\r
+        return STATUS_ERROR;\r
+      }\r
+      //\r
+      // For the first string (language name), we print out the "spacat" if they\r
+      // requested it. We set LangName to point to the proper language name string above.\r
+      //\r
+      if (StringIndex == STRING_ID_LANGUAGE_NAME) {\r
+        Offset += (wcslen (LangName) + 1) * sizeof (WCHAR);\r
+        CurrString = StringDBFindString (\r
+                      Lang->LanguageName,\r
+                      StringIdentifier->StringName,\r
+                      NULL, // scope\r
+                      NULL,\r
+                      NULL\r
+                      );\r
+      } else {\r
+        //\r
+        // Find a matching string\r
+        //\r
+        CurrString = StringDBFindString (\r
+                      Lang->LanguageName,\r
+                      StringIdentifier->StringName,\r
+                      NULL, // scope\r
+                      NULL, // LanguagesOfInterest,\r
+                      NULL\r
+                      );\r
+        //\r
+        // IndirectionList);\r
+        //\r
+        if (NULL == CurrString) {\r
+          CurrString = StringDBFindString (\r
+                        L"eng",\r
+                        StringIdentifier->StringName,\r
+                        NULL, // scope\r
+                        NULL, // LanguagesOfInterest,\r
+                        NULL\r
+                        );\r
+          //\r
+          // IndirectionList);\r
+          //\r
+        }\r
+\r
+        EmptyString.LanguageName = Lang->LanguageName;\r
+        if (CurrString == NULL) {\r
+          CurrString        = &EmptyString;\r
+          EmptyString.Flags = STRING_FLAGS_UNDEFINED;\r
+        } else if ((StringIdentifier->Flags & STRING_FLAGS_REFERENCED) == 0) {\r
+          CurrString        = &EmptyString;\r
+          EmptyString.Flags = 0;\r
+        }\r
+\r
+        Offset += CurrString->Size;\r
+      }\r
+    }\r
+\r
+    //\r
+    // PASS 3: write the strings themselves.\r
+    //\r
+    Offset = sizeof (StringPack) + mDBData.NumStringIdentifiersReferenced * sizeof (STRING_OFFSET);\r
+    for (StringIndex = 0; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {\r
+      StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex);\r
+      if (StringIdentifier == NULL) {\r
+        Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);\r
+        return STATUS_ERROR;\r
+      }\r
+      //\r
+      // For the first string (language name), we print out the "spacat" if they\r
+      // requested it. We set LangName to point to the proper language name string above.\r
+      //\r
+      if (StringIndex == STRING_ID_LANGUAGE_NAME) {\r
+        TempStringPtr = LangName;\r
+      } else {\r
+        //\r
+        // Find a matching string if this string identifier was referenced\r
+        //\r
+        CurrString = NULL;\r
+        if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {\r
+          CurrString = StringDBFindString (\r
+                        Lang->LanguageName,\r
+                        StringIdentifier->StringName,\r
+                        NULL, // scope\r
+                        NULL, // LanguagesOfInterest,\r
+                        NULL\r
+                        );\r
+          //\r
+          // IndirectionList);\r
+          //\r
+          if (NULL == CurrString) {\r
+            CurrString = StringDBFindString (\r
+                          L"eng",\r
+                          StringIdentifier->StringName,\r
+                          NULL, // scope\r
+                          NULL, // LanguagesOfInterest,\r
+                          NULL\r
+                          );\r
+            //\r
+            // IndirectionList);\r
+            //\r
+          }\r
+        }\r
+\r
+        if (CurrString == NULL) {\r
+          CurrString = &EmptyString;\r
+        }\r
+\r
+        TempStringPtr = CurrString->Str;\r
+      }\r
+\r
+      for (TempIndex = 0; TempStringPtr[TempIndex] != 0; TempIndex++) {\r
+        fwrite (&TempStringPtr[TempIndex], sizeof (CHAR16), 1, Fptr);\r
+        Offset += 2;\r
+      }\r
+      //\r
+      // Print NULL WCHAR at the end of this string.\r
+      //\r
+      TempIndex = 0;\r
+      fwrite (&TempIndex, sizeof (CHAR16), 1, Fptr);\r
+      Offset += 2;\r
+    }\r
+    //\r
+    // Sanity check the offset. Make sure our running offset is what we put in the\r
+    // string pack header.\r
+    //\r
+    if (StringPack.Header.Length != Offset) {\r
+      Error (\r
+        __FILE__,\r
+        __LINE__,\r
+        0,\r
+        "application error",\r
+        "stringpack size 0x%X does not match final size 0x%X",\r
+        StringPack.Header.Length,\r
+        Offset\r
+        );\r
+    }\r
+  }\r
+  //\r
+  // Print terminator string pack, closing brace and close the file.\r
+  // The size of 0 triggers to the consumer that this is the end.\r
+  //\r
+  memset ((char *) &StringPack, 0, sizeof (EFI_HII_STRING_PACK_HEADER));\r
+  StringPack.Header.Type = EFI_HII_STRING;\r
+  fwrite ((void *) &StringPack, sizeof (StringPack), 1, Fptr);\r
+  fclose (Fptr);\r
+  return STATUS_SUCCESS;\r
+}\r