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