mirror of https://github.com/acidanthera/audk.git
2760 lines
80 KiB
C
2760 lines
80 KiB
C
|
/*++
|
||
|
|
||
|
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;
|
||
|
}
|