]> git.proxmox.com Git - mirror_edk2.git/blobdiff - EdkCompatibilityPkg/Sample/Tools/Source/UefiStrGather/StringDB.c
1) Sync EdkCompatibilityPkg with EDK 1.04. The changes includes:
[mirror_edk2.git] / EdkCompatibilityPkg / Sample / Tools / Source / UefiStrGather / StringDB.c
diff --git a/EdkCompatibilityPkg/Sample/Tools/Source/UefiStrGather/StringDB.c b/EdkCompatibilityPkg/Sample/Tools/Source/UefiStrGather/StringDB.c
new file mode 100644 (file)
index 0000000..ac6fef8
--- /dev/null
@@ -0,0 +1,2674 @@
+/*++\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>\r
+#include <Tiano.h>\r
+#include <EfiUtilityMsgs.h>\r
+#include <EfiHii.h>\r
+#include "StrGather.h"\r
+#include "StringDb.h"\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
+void\r
+StringDBWriteStandardFileHeader (\r
+  FILE *OutFptr\r
+  );\r
+\r
+static\r
+WCHAR                 *\r
+AsciiToWchar (\r
+  INT8 *Str\r
+  );\r
+\r
+static\r
+CHAR8 *\r
+WcharToAscii (\r
+  WCHAR *Str\r
+  );\r
+\r
+static\r
+WCHAR                 *\r
+DuplicateString (\r
+  WCHAR   *Str\r
+  );\r
+\r
+static\r
+WCHAR *\r
+WstrCatenate (\r
+  WCHAR *Dst,\r
+  WCHAR *Src\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
+  WCHAR           *SecondaryLanguageList\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->SecondaryLanguageList);\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
+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
+  STRING_IDENTIFIER *StringIdentifier;\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
+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
+  WCHAR *SecondaryLanguageList\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 = (WCHAR *) malloc ((wcslen (LanguageName) + 1) * 2);\r
+       if (Lang->LanguageName == NULL) {\r
+      Error (NULL, 0, 0, NULL, "memory allocation error");\r
+      return STATUS_ERROR;\r
+    }\r
+    wcscpy (Lang->LanguageName, LanguageName);\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
+      FREE (Lang->LanguageName);\r
+      return STATUS_ERROR;\r
+    }\r
+    wcscpy (Lang->PrintableLanguageName, PrintableLanguageName);\r
+\r
+       if (SecondaryLanguageList != NULL) {\r
+      Lang->SecondaryLanguageList = (WCHAR *) malloc ((wcslen (SecondaryLanguageList) + 1) * sizeof (WCHAR));\r
+      if (Lang->SecondaryLanguageList == NULL) {\r
+        Error (NULL, 0, 0, NULL, "memory allocation error");\r
+        FREE (Lang->PrintableLanguageName);\r
+        FREE (Lang->LanguageName);\r
+        return STATUS_ERROR;\r
+      }\r
+      wcscpy (Lang->SecondaryLanguageList, SecondaryLanguageList);\r
+       } else {\r
+      Lang->SecondaryLanguageList = NULL;\r
+       }\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
+STATUS\r
+StringDBAddSecondaryLanguage (\r
+  WCHAR *LanguageName,\r
+  WCHAR *SecondaryLanguageList\r
+  )\r
+{\r
+  LANGUAGE_LIST *Lang;\r
+\r
+  Lang = StringDBFindLanguageList (LanguageName);\r
+  if (Lang == NULL) {\r
+    return STATUS_ERROR;\r
+  } else {\r
+    Lang->SecondaryLanguageList = WstrCatenate(Lang->SecondaryLanguageList, SecondaryLanguageList);\r
+    return STATUS_SUCCESS;\r
+  }\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, Lang->SecondaryLanguageList);\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
+  WCHAR           *SecondaryLanguageList\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
+  if (StringDBWriteGenericString (DBFptr, SecondaryLanguageList) != 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 = NULL;\r
+  WCHAR   *PrintableLanguageName = NULL;\r
+  WCHAR   *SecondaryLanguageList = NULL;\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
+  if (StringDBReadGenericString (DBFptr, &Size, &SecondaryLanguageList) != STATUS_SUCCESS) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  //\r
+  // printf("LANG: %S %S\n", LanguageName, PrintableLanguageName);\r
+  //\r
+  Status = StringDBAddLanguage (LanguageName, PrintableLanguageName, SecondaryLanguageList);\r
+  FREE (LanguageName);\r
+  FREE (PrintableLanguageName);\r
+  FREE (SecondaryLanguageList);\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
+WstrCatenate (\r
+  WCHAR *Dst,\r
+  WCHAR *Src\r
+  )\r
+{\r
+  UINT32 Len  = 0;\r
+  WCHAR  *Bak = Dst;\r
+\r
+  if (Src == NULL) {\r
+    return Dst;\r
+  }\r
+\r
+  if (Dst != NULL) {\r
+    Len = wcslen (Dst);\r
+  }\r
+  Len += wcslen (Src);\r
+  Dst = (WCHAR *) malloc ((Len + 1) * 2);\r
+  if (Dst == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  Dst[0] = L'\0';\r
+  if (Bak != NULL) {\r
+    wcscpy (Dst, Bak);\r
+    FREE (Bak);\r
+  }\r
+  wcscat (Dst, Src);\r
+  return Dst;\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
+static\r
+CHAR8 *\r
+WcharToAscii (\r
+  WCHAR *Str\r
+  )\r
+{\r
+  UINT32  Len;\r
+  CHAR8   *NewStr;\r
+  CHAR8   *Ptr;\r
+\r
+  Len     = wcslen (Str) + 1;\r
+  NewStr  = (CHAR8 *) malloc (Len * sizeof (CHAR8));\r
+  for (Ptr = NewStr; *Str != L'\0'; Str++, Ptr++) {\r
+    *Ptr = (CHAR8) *Str;\r
+  }\r
+\r
+  *Ptr = '\0';\r
+  return NewStr;\r
+}\r
+\r
+/*****************************************************************************/\r
+CHAR8 *\r
+unicode2ascii (\r
+  WCHAR *UnicodeStr\r
+  )\r
+{\r
+  CHAR8     *RetStr   = (CHAR8 *)UnicodeStr;\r
+  CHAR8     *AsciiStr = (CHAR8 *)UnicodeStr;\r
+\r
+  while (*UnicodeStr != '\0') {\r
+    *AsciiStr = (CHAR8) *(UnicodeStr++);\r
+    AsciiStr++;\r
+  }\r
+  *AsciiStr = '\0';\r
+\r
+  return RetStr;\r
+}\r
+\r
+STATUS\r
+BuildStringPkgHdr (\r
+  IN  WCHAR                       *PrimaryLangName,\r
+  IN  WCHAR                       *SecondaryLangList,\r
+  IN  UINT32                      Type,\r
+  IN  UINT32                      PkgBlkSize,\r
+  OUT EFI_HII_STRING_PACKAGE_HDR  **StrPkgHdr\r
+  )\r
+{\r
+  UINT32  LangNameLen;\r
+\r
+  LangNameLen = wcslen (PrimaryLangName);\r
+  if (SecondaryLangList != NULL) {\r
+    LangNameLen += wcslen (SecondaryLangList) + 1;\r
+  }\r
+\r
+  *StrPkgHdr = (EFI_HII_STRING_PACKAGE_HDR *) malloc(sizeof (EFI_HII_STRING_PACKAGE_HDR) + LangNameLen);\r
+  if (*StrPkgHdr == NULL) {\r
+    return STATUS_ERROR;\r
+  }\r
+  memset (*StrPkgHdr, 0, sizeof (EFI_HII_STRING_PACKAGE_HDR) + LangNameLen);\r
+\r
+  (*StrPkgHdr)->Header.Type       = Type;\r
+  (*StrPkgHdr)->Header.Length     = PkgBlkSize + sizeof (EFI_HII_STRING_PACKAGE_HDR) + LangNameLen;\r
+  (*StrPkgHdr)->HdrSize           = sizeof (EFI_HII_STRING_PACKAGE_HDR) + LangNameLen;\r
+  (*StrPkgHdr)->StringInfoOffset  = sizeof (EFI_HII_STRING_PACKAGE_HDR) + LangNameLen;\r
+  (*StrPkgHdr)->LanguageWindow[0] = L'\0';\r
+  (*StrPkgHdr)->LanguageName      = (EFI_STRING_ID)1;\r
+\r
+  strcpy ((*StrPkgHdr)->Language, unicode2ascii(PrimaryLangName));\r
+  if (SecondaryLangList != NULL) {\r
+    strcat ((*StrPkgHdr)->Language, ";");\r
+    strcat ((*StrPkgHdr)->Language, unicode2ascii(SecondaryLangList));\r
+  }\r
+\r
+#ifdef DEBUG_STRGATHER\r
+  printf ("STR HDR\t %s\n", (*StrPkgHdr)->Language);\r
+#endif\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+STATUS\r
+BuildStringPkgUCS2Blk (\r
+  IN  EFI_STRING_ID                   StringId,\r
+  IN  WCHAR                           *LangName,\r
+  IN  WCHAR                           *StrName,\r
+  OUT EFI_HII_SIBT_STRING_UCS2_BLOCK  **StrBlk,\r
+  OUT UINT32                          *BlkSize\r
+  )\r
+{\r
+  UINT32      StrLen      = 0;\r
+  STRING_LIST *CurrString = NULL;\r
+\r
+  if ((LangName == NULL) || (StrName == NULL) || (StrBlk == NULL)) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  *StrBlk  = NULL;\r
+  *BlkSize = 0;\r
+\r
+  CurrString = StringDBFindString (LangName, StrName, NULL, NULL, NULL);\r
+  if (CurrString == NULL) {\r
+       return STATUS_WARNING;\r
+  }\r
+\r
+  StrLen = wcslen (CurrString->Str);\r
+  *BlkSize = sizeof (EFI_HII_SIBT_STRING_UCS2_BLOCK) + StrLen * 2;\r
+  *StrBlk  = (EFI_HII_SIBT_STRING_UCS2_BLOCK *) malloc (*BlkSize);\r
+  if (*StrBlk == NULL) {\r
+    *StrBlk  = NULL;\r
+    *BlkSize = 0;\r
+    return STATUS_ERROR;\r
+  }\r
+  (*StrBlk)->Header.BlockType = EFI_HII_SIBT_STRING_UCS2;\r
+  wcscpy((*StrBlk)->StringText, CurrString->Str);\r
+\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+STATUS\r
+BuildStringPkgSKIP2Blk (\r
+  IN  EFI_STRING_ID                   SkipIdCount,\r
+  OUT EFI_HII_SIBT_SKIP2_BLOCK        **StrBlk\r
+  )\r
+{\r
+  if (StrBlk == NULL) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  *StrBlk  = NULL;\r
+\r
+  *StrBlk  = (EFI_HII_SIBT_SKIP2_BLOCK *) malloc (sizeof (EFI_HII_SIBT_SKIP2_BLOCK));\r
+  if (*StrBlk == NULL) {\r
+    *StrBlk  = NULL;\r
+    return STATUS_ERROR;\r
+  }\r
+  (*StrBlk)->Header.BlockType = EFI_HII_SIBT_SKIP2;\r
+  (*StrBlk)->SkipCount        = SkipIdCount;\r
+\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+STATUS\r
+BuildStringPkgEndBlk (\r
+  OUT EFI_HII_SIBT_END_BLOCK **End\r
+  )\r
+{\r
+  *End = (EFI_HII_SIBT_END_BLOCK *) malloc (sizeof (EFI_HII_SIBT_END_BLOCK));\r
+  if (*End == NULL) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  (*End)->Header.BlockType = EFI_HII_SIBT_END;\r
+  return STATUS_SUCCESS;\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
+StrPkgBlkBufferListAddTail (\r
+  IN EFI_STRING_ID      StringId,\r
+  IN WCHAR              *StrName,\r
+  IN SPkgBlkBuffer      **PkgBufferListHead,\r
+  IN SPkgBlkBuffer      **PkgBufferListTail,\r
+  IN VOID               *Buffer,\r
+  IN UINT32             Size \r
+  )\r
+{\r
+  SPkgBlkBuffer         *pNew = NULL;\r
+#ifdef DEBUG_STRGATHER\r
+  EFI_HII_STRING_BLOCK  *SBlk = (EFI_HII_STRING_BLOCK *)Buffer;\r
+#endif\r
+\r
+  if ((PkgBufferListHead == NULL) || (PkgBufferListTail == NULL)) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  pNew = (SPkgBlkBuffer *) malloc (sizeof (SPkgBlkBuffer));\r
+  if (pNew == NULL) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  pNew->mBlkBuffer = Buffer;\r
+  pNew->mBlkSize   = Size;\r
+  if ((*PkgBufferListTail) == NULL) {\r
+    (*PkgBufferListHead) = (*PkgBufferListTail) = pNew;\r
+  } else {\r
+    (*PkgBufferListTail)->mNext = pNew;\r
+    (*PkgBufferListTail) = pNew;\r
+    pNew->mNext = NULL;\r
+  }\r
+\r
+#ifdef DEBUG_STRGATHER\r
+  switch (SBlk->BlockType) {\r
+  case EFI_HII_SIBT_STRING_UCS2 :\r
+    printf ("\tID: [%x] TYPE: [UCS2]\t NAME: %S \t STR: %S\n", StringId, StrName, ((EFI_HII_SIBT_STRING_UCS2_BLOCK *)SBlk)->StringText);\r
+    break;\r
+  case EFI_HII_SIBT_SKIP2 :\r
+    printf ("\tID: [NULL] TYPE: [SKIP2] SKIPCOUNT: [%x]\n", ((EFI_HII_SIBT_SKIP2_BLOCK *)SBlk)->SkipCount);\r
+    break;\r
+  case EFI_HII_SIBT_END :\r
+    printf ("\tID: [%x] TYPE: [END]\n", StringId);\r
+    break;\r
+  default :\r
+    printf ("!!!!UNKNOWN STRING TYPE!!!\n");\r
+  }\r
+#endif\r
+\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+VOID\r
+StrPkgHdrFree (\r
+  IN EFI_HII_STRING_PACKAGE_HDR *StrPkgHdr\r
+  )\r
+{\r
+  if (StrPkgHdr != NULL) {\r
+    free (StrPkgHdr);\r
+  }\r
+}\r
+\r
+VOID\r
+StrPkgBlkBufferListFree (\r
+  IN SPkgBlkBuffer *PkgBlkList\r
+  )\r
+{\r
+  SPkgBlkBuffer  *Buffer;\r
+\r
+  while (PkgBlkList != NULL) {\r
+    Buffer      = PkgBlkList;\r
+    PkgBlkList = PkgBlkList->mNext;\r
+\r
+    if (Buffer->mBlkBuffer != NULL) {\r
+      free (Buffer->mBlkBuffer);\r
+    }\r
+    free (Buffer);\r
+  }\r
+}\r
+\r
+VOID\r
+WriteBlockLine (\r
+  IN FILE   *pFile,\r
+  IN UINT32 LineBytes,\r
+  IN INT8   *LineHeader,\r
+  IN INT8   *BlkBuf,\r
+  IN UINT32 BlkSize\r
+  )\r
+{\r
+  UINT32    Index;\r
+\r
+  if ((pFile == NULL) || (LineHeader == NULL) || (BlkBuf == NULL)) {\r
+    return;\r
+  }\r
+\r
+  for (Index = 0; Index < BlkSize; Index++) {\r
+    if ((Index % LineBytes) == 0) {\r
+      fprintf (pFile, "\n%s", LineHeader);\r
+    }\r
+    fprintf (pFile, "0x%02X,  ", (UINT8)BlkBuf[Index]);\r
+  }\r
+}\r
+\r
+VOID\r
+WriteBlockEnd (\r
+  IN FILE   *pFile,\r
+  IN UINT32 LineBytes,\r
+  IN INT8   *LineHeader,\r
+  IN INT8   *BlkBuf,\r
+  IN UINT32 BlkSize\r
+  )\r
+{\r
+  UINT32    Index;\r
+\r
+  if ((BlkSize == 0) || (pFile == NULL) || (LineHeader == NULL) || (BlkBuf == NULL)) {\r
+    return;\r
+  }\r
+\r
+  for (Index = 0; Index < BlkSize - 1; Index++) {\r
+    if ((Index % LineBytes) == 0) {\r
+      fprintf (pFile, "\n%s", LineHeader);\r
+    }\r
+    fprintf (pFile, "0x%02X,  ", (UINT8)BlkBuf[Index]);\r
+  }\r
+\r
+  if ((Index % LineBytes) == 0) {\r
+    fprintf (pFile, "\n%s", LineHeader);\r
+  }\r
+  fprintf (pFile, "0x%02X\n", (UINT8)BlkBuf[Index]);\r
+}\r
+\r
+#define BYTES_PRE_LINE 0x10\r
+\r
+VOID\r
+StrPkgWriteHdrCFile (\r
+  IN FILE                       *File,\r
+  IN EFI_HII_STRING_PACKAGE_HDR *StrPkgHdr\r
+  )\r
+{\r
+  if (StrPkgHdr != NULL) {\r
+    fprintf (File, "\n  // PACKAGE HEADER\n");\r
+    WriteBlockLine(File, BYTES_PRE_LINE, "  ", (INT8 *)StrPkgHdr, StrPkgHdr->HdrSize);\r
+  }\r
+}\r
+\r
+VOID\r
+StrPkgWirteArrayLength (\r
+  IN FILE                       *File,\r
+  IN UINT32                     PkgNumber,\r
+  IN EFI_HII_STRING_PACKAGE_HDR **PkgHdr\r
+  )\r
+{\r
+  UINT32                        Index;\r
+  UINT32                        ArrayLen;\r
+\r
+  ArrayLen = sizeof (UINT32);\r
+  for (Index = 0; Index < PkgNumber; Index++) {\r
+    if (PkgHdr[Index] != NULL) {\r
+      ArrayLen += PkgHdr[Index]->Header.Length;\r
+    }\r
+  }\r
+\r
+  fprintf (File, "\n  // STRING ARRAY LENGTH\n");\r
+  WriteBlockLine(File, BYTES_PRE_LINE, "  ", (UINT8 *)&ArrayLen, sizeof (UINT32));\r
+}\r
+\r
+VOID\r
+StrPkgWriteBlkListCFile (\r
+  IN FILE                       *File,\r
+  IN SPkgBlkBuffer              *BlkList,\r
+  IN BOOLEAN                    WriteEnd\r
+  )\r
+{\r
+  SPkgBlkBuffer  *Buffer;\r
+\r
+  fprintf (File, "\n\n  // PACKAGE DATA\n");\r
+\r
+  while (BlkList != NULL) {\r
+    Buffer   = BlkList;\r
+    BlkList = BlkList->mNext;\r
+\r
+    if ((Buffer->mNext == NULL) && (WriteEnd == TRUE)) {\r
+      if (Buffer->mBlkBuffer != NULL) {\r
+        WriteBlockEnd (File, BYTES_PRE_LINE, "  ", Buffer->mBlkBuffer, Buffer->mBlkSize);\r
+      }\r
+    } else {\r
+      if (Buffer->mBlkBuffer != NULL) {\r
+        WriteBlockLine(File, BYTES_PRE_LINE, "  ", Buffer->mBlkBuffer, Buffer->mBlkSize);\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+VOID\r
+StrPkgWriteHdrBinary (\r
+  IN FILE                       *File,\r
+  IN EFI_HII_STRING_PACKAGE_HDR *StrPkgHdr\r
+  )\r
+{\r
+  fwrite (StrPkgHdr, StrPkgHdr->HdrSize, 1, File);\r
+}\r
+\r
+VOID\r
+StrPkgWriteBlkListBinary (\r
+  IN FILE                       *File,\r
+  IN SPkgBlkBuffer              *BlkList\r
+  )\r
+{\r
+  SPkgBlkBuffer  *Buffer;\r
+\r
+  while (BlkList != NULL) {\r
+    Buffer   = BlkList;\r
+    BlkList = BlkList->mNext;\r
+\r
+    if (Buffer->mBlkBuffer != NULL) {\r
+      fwrite (Buffer->mBlkBuffer, Buffer->mBlkSize, 1, File);\r
+    }\r
+  }\r
+}\r
+\r
+STATUS\r
+StringDBGenStrPkgHdrAndBlkList (\r
+  IN  LANGUAGE_LIST              *Lang,\r
+  OUT EFI_HII_STRING_PACKAGE_HDR **StrPkgHdr,\r
+  OUT SPkgBlkBuffer              **BlkList\r
+  )\r
+{\r
+  STATUS                          Status;\r
+  UINT32                          StringIndex;\r
+  EFI_STRING_ID                   StringIdCurrent;\r
+  EFI_STRING_ID                   SkipIdCount;\r
+  UINT32                          BlkSize = 0;\r
+  EFI_HII_SIBT_STRING_UCS2_BLOCK  *StrUCS2Blk  = NULL;\r
+  EFI_HII_SIBT_SKIP2_BLOCK        *StrSKIP2Blk = NULL;\r
+  STRING_IDENTIFIER               *StringIdentifier = NULL;\r
+  EFI_HII_SIBT_END_BLOCK          *EndBlk = NULL;\r
+  UINT32                          PkgBlkSize = 0;\r
+  SPkgBlkBuffer                   *PkgBufferListHead = NULL;\r
+  SPkgBlkBuffer                   *PkgBufferListTail = NULL;\r
+\r
+  if ((Lang == NULL) || (StrPkgHdr == NULL) || (BlkList == NULL)) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  //\r
+  // Assign index values to the string identifiers\r
+  //\r
+  StringDBAssignStringIndexes ();\r
+  StringIdCurrent = EFI_STRING_ID_BEGIN;\r
+  SkipIdCount     = 0;\r
+\r
+  for (StringIndex = STRING_ID_PRINTABLE_LANGUAGE_NAME; StringIndex < mDBData.NumStringIdentifiersReferenced; StringIndex++) {\r
+    if ((StringIdentifier = StringDBFindStringIdentifierByIndex (StringIndex)) == NULL) {\r
+      Error (NULL, 0, 0, "internal error", "invalid string index 0x%X", StringIndex);\r
+      goto ExportPackOut;\r
+    }\r
+\r
+    if (StringIdentifier->Flags & STRING_FLAGS_REFERENCED) {\r
+      Status = BuildStringPkgUCS2Blk (StringIdCurrent, Lang->LanguageName, StringIdentifier->StringName, &StrUCS2Blk, &BlkSize);\r
+      switch (Status) {\r
+      case STATUS_ERROR: \r
+        goto ExportPackOut; \r
+        break;\r
+      case STATUS_WARNING :\r
+        SkipIdCount++;\r
+        break;\r
+      case STATUS_SUCCESS :\r
+        if (SkipIdCount == 0) {\r
+          if (StrPkgBlkBufferListAddTail (\r
+                StringIdCurrent, \r
+                StringIdentifier->StringName, \r
+                &PkgBufferListHead, \r
+                &PkgBufferListTail, \r
+                StrUCS2Blk, \r
+                BlkSize\r
+                ) != STATUS_SUCCESS) {\r
+            goto ExportPackOut;\r
+          }\r
+          PkgBlkSize += BlkSize;\r
+        } else {\r
+          if (BuildStringPkgSKIP2Blk (SkipIdCount, &StrSKIP2Blk) != STATUS_SUCCESS) {\r
+            goto ExportPackOut;\r
+          } else {\r
+            if (StrPkgBlkBufferListAddTail (\r
+                StringIdCurrent, \r
+                NULL, \r
+                &PkgBufferListHead, \r
+                &PkgBufferListTail, \r
+                StrSKIP2Blk, \r
+                sizeof (EFI_HII_SIBT_SKIP2_BLOCK)\r
+                ) != STATUS_SUCCESS) {\r
+              goto ExportPackOut;\r
+            }\r
+            PkgBlkSize += sizeof (EFI_HII_SIBT_SKIP2_BLOCK);\r
+            SkipIdCount = 0;\r
+          }\r
+\r
+          if (StrPkgBlkBufferListAddTail (\r
+                StringIdCurrent, \r
+                StringIdentifier->StringName, \r
+                &PkgBufferListHead, \r
+                &PkgBufferListTail, \r
+                StrUCS2Blk, \r
+                BlkSize\r
+                ) != STATUS_SUCCESS) {\r
+            goto ExportPackOut;\r
+          }\r
+          PkgBlkSize += BlkSize;\r
+        }\r
+      }\r
+    }\r
+\r
+    StringIdCurrent++;\r
+  }\r
+\r
+  if (SkipIdCount != 0) {\r
+    if (BuildStringPkgSKIP2Blk (SkipIdCount, &StrSKIP2Blk) != STATUS_SUCCESS) {\r
+      goto ExportPackOut;\r
+    } else {\r
+      if (StrPkgBlkBufferListAddTail (\r
+            StringIdCurrent, \r
+            NULL, \r
+            &PkgBufferListHead, \r
+            &PkgBufferListTail, \r
+            StrSKIP2Blk, \r
+            sizeof (EFI_HII_SIBT_SKIP2_BLOCK)\r
+            ) != STATUS_SUCCESS) {\r
+        goto ExportPackOut;\r
+      }\r
+      PkgBlkSize += sizeof (EFI_HII_SIBT_SKIP2_BLOCK);\r
+      SkipIdCount = 0;\r
+    }\r
+  }\r
+\r
+  if (BuildStringPkgEndBlk (&EndBlk) != STATUS_SUCCESS) {\r
+    goto ExportPackOut;\r
+  } else if (StrPkgBlkBufferListAddTail (\r
+               StringIdCurrent, \r
+               NULL, \r
+               &PkgBufferListHead, \r
+               &PkgBufferListTail, \r
+               EndBlk, \r
+               sizeof (EFI_HII_SIBT_END_BLOCK)\r
+               ) != STATUS_SUCCESS) {\r
+    goto ExportPackOut;\r
+  }\r
+  StringIdCurrent++;\r
+  PkgBlkSize += sizeof (EFI_HII_SIBT_END_BLOCK);\r
+\r
+  if (BuildStringPkgHdr(\r
+        Lang->LanguageName, \r
+        Lang->SecondaryLanguageList,\r
+        EFI_HII_PACKAGE_STRINGS, \r
+        PkgBlkSize, \r
+        StrPkgHdr\r
+        ) != STATUS_SUCCESS) {\r
+    goto ExportPackOut;\r
+  }\r
+\r
+  *BlkList   = PkgBufferListHead;\r
+\r
+  return STATUS_SUCCESS;\r
+\r
+ExportPackOut:\r
+  StrPkgBlkBufferListFree(PkgBufferListHead);\r
+  *BlkList   = NULL;\r
+  *StrPkgHdr = NULL;\r
+  return STATUS_ERROR;\r
+}\r
+\r
+STATUS\r
+StringDBCreateHiiExportPack (\r
+  INT8                        *FileName\r
+  )\r
+{\r
+  FILE                            *File       = NULL;\r
+  LANGUAGE_LIST                   *Lang       = NULL;\r
+  EFI_HII_STRING_PACKAGE_HDR      *StrPkgHdr  = NULL;\r
+  SPkgBlkBuffer                   *BlkList    = NULL;\r
+\r
+  if (FileName == NULL) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if ((File = fopen (FileName, "wb")) == NULL) {\r
+    Error (NULL, 0, 0, FileName, "failed to open output HII export file");\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  for (Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next) {\r
+    if (StringDBGenStrPkgHdrAndBlkList(Lang, &StrPkgHdr, &BlkList) != STATUS_SUCCESS) {\r
+      fclose (File);\r
+      return STATUS_SUCCESS;\r
+    }\r
+\r
+    StrPkgWriteHdrBinary (File, StrPkgHdr);\r
+    StrPkgWriteBlkListBinary (File, BlkList);\r
+\r
+    StrPkgHdrFree (StrPkgHdr);\r
+    StrPkgBlkBufferListFree (BlkList);\r
+  }\r
+\r
+  fclose (File);\r
+  return STATUS_SUCCESS;\r
+}\r
+\r
+static const char *gSourceFileHeader[] = {\r
+  "//",\r
+  "//  DO NOT EDIT -- auto-generated file",\r
+  "//",\r
+  "//  This file is generated by the StrGather utility",\r
+  "//",\r
+  NULL\r
+};\r
+\r
+STATUS\r
+StringDBDumpCStrings (\r
+  INT8                            *BaseName,\r
+  INT8                            *FileName\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  FILE                            *File       = NULL;\r
+  LANGUAGE_LIST                   *Lang       = NULL;\r
+  EFI_HII_STRING_PACKAGE_HDR      **StrPkgHdr = NULL;\r
+  SPkgBlkBuffer                   **BlkList   = NULL;\r
+  UINT32                          Index;\r
+  UINT32                          LangNumber  = 0;\r
+\r
+  if ((BaseName == NULL) || (FileName == NULL)) {\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  if (mDBData.LanguageList == NULL) {\r
+    return STATUS_SUCCESS;\r
+  }\r
+\r
+  for (LangNumber = 0, Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next, LangNumber++)\r
+    ;\r
+\r
+  StrPkgHdr = (EFI_HII_STRING_PACKAGE_HDR **) malloc (sizeof (EFI_HII_STRING_PACKAGE_HDR *) * LangNumber);\r
+  BlkList = (SPkgBlkBuffer **) malloc (sizeof (SPkgBlkBuffer *) * LangNumber);\r
+  for (Index = 0; Index < LangNumber; Index++) {\r
+    StrPkgHdr[Index] = NULL;\r
+    BlkList[Index] = NULL;\r
+  }\r
+\r
+  for (Index = 0, Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next, Index++) {\r
+    Status = StringDBGenStrPkgHdrAndBlkList(Lang, &StrPkgHdr[Index], &BlkList[Index]);\r
+    if (EFI_ERROR(Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  if ((File = fopen (FileName, "w")) == NULL) {\r
+    Error (NULL, 0, 0, FileName, "failed to open output C file - %s", FileName);\r
+    return STATUS_ERROR;\r
+  }\r
+\r
+  for (Index = 0; gSourceFileHeader[Index] != NULL; Index++) {\r
+    fprintf (File, "%s\n", gSourceFileHeader[Index]);\r
+  }\r
+\r
+  fprintf (File, "\nunsigned char %s[] = {\n", BaseName);\r
+\r
+  //\r
+  // Save the length of the string package array.\r
+  //\r
+  StrPkgWirteArrayLength (File, LangNumber, StrPkgHdr);\r
+\r
+  for (Index = 0, Lang = mDBData.LanguageList; Lang != NULL; Lang = Lang->Next, Index++) {\r
+    if (StrPkgHdr[Index] != NULL) {\r
+      StrPkgWriteHdrCFile (File, StrPkgHdr[Index]);\r
+      StrPkgWriteBlkListCFile (File, BlkList[Index], (Lang->Next == NULL) ? TRUE : FALSE);\r
+    }\r
+\r
+    StrPkgHdrFree (StrPkgHdr[Index]);\r
+    StrPkgBlkBufferListFree (BlkList[Index]);\r
+  }\r
+\r
+  fprintf (File, "\n};\n");\r
+\r
+  fclose (File);\r
+  return STATUS_SUCCESS;\r
+}\r