#undef DEBUG
-static WapiImageSectionHeader *get_enclosing_section_header (guint32 rva, WapiImageNTHeaders *nt_headers)
+#define ALIGN32(ptr) ptr = (gpointer)((char *)ptr + 3); ptr = (gpointer)((char *)ptr - ((gsize)ptr & 3));
+
+static WapiImageSectionHeader *get_enclosing_section_header (guint32 rva, WapiImageNTHeaders32 *nt_headers)
{
- WapiImageSectionHeader *section = IMAGE_FIRST_SECTION (nt_headers);
+ WapiImageSectionHeader *section = _WAPI_IMAGE_FIRST_SECTION32 (nt_headers);
guint32 i;
for (i = 0; i < GUINT16_FROM_LE (nt_headers->FileHeader.NumberOfSections); i++, section++) {
return(NULL);
}
-static gpointer get_ptr_from_rva (guint32 rva, WapiImageNTHeaders *ntheaders,
+/* This works for both 32bit and 64bit files, as the differences are
+ * all after the section header block
+ */
+static gpointer get_ptr_from_rva (guint32 rva, WapiImageNTHeaders32 *ntheaders,
gpointer file_map)
{
WapiImageSectionHeader *section_header;
return(NULL);
}
- delta = (guint32)(section_header->VirtualAddress -
- section_header->PointerToRawData);
+ delta = (guint32)(GUINT32_FROM_LE (section_header->VirtualAddress) -
+ GUINT32_FROM_LE (section_header->PointerToRawData));
return((guint8 *)file_map + rva - delta);
}
static gpointer scan_resource_dir (WapiImageResourceDirectory *root,
- WapiImageNTHeaders *nt_headers,
+ WapiImageNTHeaders32 *nt_headers,
gpointer file_map,
WapiImageResourceDirectoryEntry *entry,
int level, guint32 res_id, guint32 lang_id,
guint32 *size)
{
- gboolean is_string = entry->NameIsString;
- gboolean is_dir = entry->DataIsDirectory;
- guint32 name_offset = GUINT32_FROM_LE (entry->NameOffset);
- guint32 dir_offset = GUINT32_FROM_LE (entry->OffsetToDirectory);
- guint32 data_offset = GUINT32_FROM_LE (entry->OffsetToData);
+ WapiImageResourceDirectoryEntry swapped_entry;
+ gboolean is_string, is_dir;
+ guint32 name_offset, dir_offset, data_offset;
+
+ swapped_entry.Name = GUINT32_FROM_LE (entry->Name);
+ swapped_entry.OffsetToData = GUINT32_FROM_LE (entry->OffsetToData);
+
+ is_string = swapped_entry.NameIsString;
+ is_dir = swapped_entry.DataIsDirectory;
+ name_offset = swapped_entry.NameOffset;
+ dir_offset = swapped_entry.OffsetToDirectory;
+ data_offset = swapped_entry.OffsetToData;
if (level == 0) {
/* Normally holds a directory entry for each type of
WapiImageResourceDataEntry *data_entry = (WapiImageResourceDataEntry *)((guint8 *)root + data_offset);
*size = GUINT32_FROM_LE (data_entry->Size);
- return(get_ptr_from_rva (data_entry->OffsetToData, nt_headers, file_map));
+ return(get_ptr_from_rva (GUINT32_FROM_LE (data_entry->OffsetToData), nt_headers, file_map));
}
}
-static gpointer find_pe_file_resources (gpointer file_map, guint32 map_size,
- guint32 res_id, guint32 lang_id,
- guint32 *size)
+static gpointer find_pe_file_resources32 (gpointer file_map, guint32 map_size,
+ guint32 res_id, guint32 lang_id,
+ guint32 *size)
{
WapiImageDosHeader *dos_header;
- WapiImageNTHeaders *nt_headers;
+ WapiImageNTHeaders32 *nt_headers;
WapiImageResourceDirectory *resource_dir;
WapiImageResourceDirectoryEntry *resource_dir_entry;
guint32 resource_rva, entries, i;
return(NULL);
}
- if (map_size < sizeof(WapiImageNTHeaders) + GUINT32_FROM_LE (dos_header->e_lfanew)) {
+ if (map_size < sizeof(WapiImageNTHeaders32) + GUINT32_FROM_LE (dos_header->e_lfanew)) {
#ifdef DEBUG
g_message ("%s: File is too small: %d", __func__, map_size);
#endif
return(NULL);
}
- nt_headers = (WapiImageNTHeaders *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew));
+ nt_headers = (WapiImageNTHeaders32 *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew));
if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
#ifdef DEBUG
g_message ("%s: Bad NT signature 0x%x", __func__,
return(NULL);
}
- resource_dir = (WapiImageResourceDirectory *)get_ptr_from_rva (resource_rva, nt_headers, file_map);
+ resource_dir = (WapiImageResourceDirectory *)get_ptr_from_rva (resource_rva, (WapiImageNTHeaders32 *)nt_headers, file_map);
if (resource_dir == NULL) {
#ifdef DEBUG
g_message ("%s: Can't find resource directory", __func__);
for (i = 0; i < entries; i++) {
WapiImageResourceDirectoryEntry *direntry = &resource_dir_entry[i];
- ret = scan_resource_dir (resource_dir, nt_headers, file_map,
- direntry, 0, res_id, lang_id, size);
+ ret = scan_resource_dir (resource_dir,
+ (WapiImageNTHeaders32 *)nt_headers,
+ file_map, direntry, 0, res_id,
+ lang_id, size);
if (ret != NULL) {
return(ret);
}
}
+
+ return(NULL);
+}
+
+static gpointer find_pe_file_resources64 (gpointer file_map, guint32 map_size,
+ guint32 res_id, guint32 lang_id,
+ guint32 *size)
+{
+ WapiImageDosHeader *dos_header;
+ WapiImageNTHeaders64 *nt_headers;
+ WapiImageResourceDirectory *resource_dir;
+ WapiImageResourceDirectoryEntry *resource_dir_entry;
+ guint32 resource_rva, entries, i;
+ gpointer ret = NULL;
+
+ dos_header = (WapiImageDosHeader *)file_map;
+ if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
+#ifdef DEBUG
+ g_message ("%s: Bad dos signature 0x%x", __func__,
+ dos_header->e_magic);
+#endif
+
+ SetLastError (ERROR_INVALID_DATA);
+ return(NULL);
+ }
+
+ if (map_size < sizeof(WapiImageNTHeaders64) + GUINT32_FROM_LE (dos_header->e_lfanew)) {
+#ifdef DEBUG
+ g_message ("%s: File is too small: %d", __func__, map_size);
+#endif
+
+ SetLastError (ERROR_BAD_LENGTH);
+ return(NULL);
+ }
+
+ nt_headers = (WapiImageNTHeaders64 *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew));
+ if (nt_headers->Signature != IMAGE_NT_SIGNATURE) {
+#ifdef DEBUG
+ g_message ("%s: Bad NT signature 0x%x", __func__,
+ nt_headers->Signature);
+#endif
+
+ SetLastError (ERROR_INVALID_DATA);
+ return(NULL);
+ }
+
+ if (nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ /* Do 64-bit stuff */
+ resource_rva = GUINT32_FROM_LE (((WapiImageNTHeaders64 *)nt_headers)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
+ } else {
+ resource_rva = GUINT32_FROM_LE (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
+ }
+
+ if (resource_rva == 0) {
+#ifdef DEBUG
+ g_message ("%s: No resources in file!", __func__);
+#endif
+ SetLastError (ERROR_INVALID_DATA);
+ return(NULL);
+ }
+ resource_dir = (WapiImageResourceDirectory *)get_ptr_from_rva (resource_rva, (WapiImageNTHeaders32 *)nt_headers, file_map);
+ if (resource_dir == NULL) {
+#ifdef DEBUG
+ g_message ("%s: Can't find resource directory", __func__);
+#endif
+ SetLastError (ERROR_INVALID_DATA);
+ return(NULL);
+ }
+
+ entries = GUINT16_FROM_LE (resource_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (resource_dir->NumberOfIdEntries);
+ resource_dir_entry = (WapiImageResourceDirectoryEntry *)(resource_dir + 1);
+
+ for (i = 0; i < entries; i++) {
+ WapiImageResourceDirectoryEntry *direntry = &resource_dir_entry[i];
+ ret = scan_resource_dir (resource_dir,
+ (WapiImageNTHeaders32 *)nt_headers,
+ file_map, direntry, 0, res_id,
+ lang_id, size);
+ if (ret != NULL) {
+ return(ret);
+ }
+ }
+
return(NULL);
}
+static gpointer find_pe_file_resources (gpointer file_map, guint32 map_size,
+ guint32 res_id, guint32 lang_id,
+ guint32 *size)
+{
+ /* Figure this out when we support 64bit PE files */
+ if (1) {
+ return find_pe_file_resources32 (file_map, map_size, res_id,
+ lang_id, size);
+ } else {
+ return find_pe_file_resources64 (file_map, map_size, res_id,
+ lang_id, size);
+ }
+}
+
static gpointer map_pe_file (gunichar2 *filename, guint32 *map_size)
{
gchar *filename_ext;
/* Check basic file size */
if (statbuf.st_size < sizeof(WapiImageDosHeader)) {
#ifdef DEBUG
- g_message ("%s: File %s is too small: %ld", __func__,
+ g_message ("%s: File %s is too small: %lld", __func__,
filename_ext, statbuf.st_size);
#endif
munmap (file_map, map_size);
}
-guint32 GetFileVersionInfoSize (gunichar2 *filename, guint32 *handle)
-{
- gpointer file_map;
- gpointer versioninfo;
- guint32 map_size;
- guint32 size;
-
- /* This value is unused, but set to zero */
- *handle = 0;
-
- file_map = map_pe_file (filename, &map_size);
- if (file_map == NULL) {
- return(0);
- }
-
- versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION,
- 0, &size);
- if (versioninfo == NULL) {
- /* Didn't find the resource, so set the return value
- * to 0
- */
- size = 0;
- }
-
- unmap_pe_file (file_map, map_size);
-
- return(size);
-}
-
-gboolean GetFileVersionInfo (gunichar2 *filename, guint32 handle G_GNUC_UNUSED,
- guint32 len, gpointer data)
-{
- gpointer file_map;
- gpointer versioninfo;
- guint32 map_size;
- guint32 size;
- gboolean ret = FALSE;
-
- file_map = map_pe_file (filename, &map_size);
- if (file_map == NULL) {
- return(FALSE);
- }
-
- versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION,
- 0, &size);
- if (versioninfo != NULL) {
- /* This could probably process the data so that
- * VerQueryValue() doesn't have to follow the data
- * blocks every time. But hey, these functions aren't
- * likely to appear in many profiles.
- */
- memcpy (data, versioninfo, len < size?len:size);
- ret = TRUE;
- }
-
- unmap_pe_file (file_map, map_size);
-
- return(ret);
-}
-
static guint32 unicode_chars (const gunichar2 *str)
{
guint32 len = 0;
static gboolean unicode_compare (const gunichar2 *str1, const gunichar2 *str2)
{
while (*str1 && *str2) {
- if (GUINT16_TO_LE (*str1) != GUINT16_TO_LE (*str2)) {
+ if (*str1 != *str2) {
return(FALSE);
}
++str1;
data = ((gunichar2 *)data) + (unicode_chars (block->key) + 1);
/* align on a 32-bit boundary */
- data = (gpointer)((char *)data + 3);
- data = (gpointer)((char *)data - (GPOINTER_TO_INT (data) & 3));
+ ALIGN32 (data);
return(data);
}
{
guint16 data_len = block->data_len;
guint16 string_len = 28; /* Length of the StringTable block */
-
+ char *orig_data_ptr = (char *)data_ptr - 28;
+
/* data_ptr is pointing at an array of one or more String blocks
* with total length (not including alignment padding) of
* data_len
*/
- while (string_len < data_len) {
- gunichar2 *value;
-
+ while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) {
/* align on a 32-bit boundary */
- data_ptr = (gpointer)((char *)data_ptr + 3);
- data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3));
+ ALIGN32 (data_ptr);
data_ptr = get_versioninfo_block (data_ptr, block);
if (block->data_len == 0) {
}
string_len = string_len + block->data_len;
- value = (gunichar2 *)data_ptr;
if (string_key != NULL &&
string_value != NULL &&
* NULL if the data read hits padding. We can't recover from this
* because the data length does not include padding bytes, so it's not
* possible to just return the start position + length
+ *
+ * If lang == NULL it means we're just stepping through this block
*/
static gconstpointer get_stringtable_block (gconstpointer data_ptr,
gchar *lang,
while(string_len < data_len) {
/* align on a 32-bit boundary */
- data_ptr = (gpointer)((char *)data_ptr + 3);
- data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3));
+ ALIGN32 (data_ptr);
data_ptr = get_versioninfo_block (data_ptr, block);
if (block->data_len == 0) {
g_strdown (found_lang);
- if (!strcmp (found_lang, lang)) {
+ if (lang != NULL && !strcmp (found_lang, lang)) {
/* Got the one we're interested in */
data_ptr = get_string_block (data_ptr, string_key,
string_value,
return(data_ptr);
}
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+static gconstpointer big_up_string_block (gconstpointer data_ptr,
+ version_data *block)
+{
+ guint16 data_len = block->data_len;
+ guint16 string_len = 28; /* Length of the StringTable block */
+ gchar *big_value;
+ char *orig_data_ptr = (char *)data_ptr - 28;
+
+ /* data_ptr is pointing at an array of one or more String
+ * blocks with total length (not including alignment padding)
+ * of data_len
+ */
+ while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) {
+ /* align on a 32-bit boundary */
+ ALIGN32 (data_ptr);
+
+ data_ptr = get_versioninfo_block (data_ptr, block);
+ if (block->data_len == 0) {
+ /* We must have hit padding, so give up
+ * processing now
+ */
+#ifdef DEBUG
+ g_message ("%s: Hit 0-length block, giving up",
+ __func__);
+#endif
+ return(NULL);
+ }
+
+ string_len = string_len + block->data_len;
+
+ big_value = g_convert ((gchar *)block->key,
+ unicode_chars (block->key) * 2,
+ "UTF-16BE", "UTF-16LE", NULL, NULL,
+ NULL);
+ if (big_value == NULL) {
+#ifdef DEBUG
+ g_message ("%s: Didn't find a valid string, giving up",
+ __func__);
+#endif
+ return(NULL);
+ }
+
+ /* The swapped string should be exactly the same
+ * length as the original little-endian one, but only
+ * copy the number of original chars just to be on the
+ * safe side
+ */
+ memcpy (block->key, big_value, unicode_chars (block->key) * 2);
+ g_free (big_value);
+
+ big_value = g_convert ((gchar *)data_ptr,
+ unicode_chars (data_ptr) * 2,
+ "UTF-16BE", "UTF-16LE", NULL, NULL,
+ NULL);
+ if (big_value == NULL) {
+#ifdef DEBUG
+ g_message ("%s: Didn't find a valid data string, giving up", __func__);
+#endif
+ return(NULL);
+ }
+ memcpy ((gpointer)data_ptr, big_value,
+ unicode_chars (data_ptr) * 2);
+ g_free (big_value);
+
+ data_ptr = ((gunichar2 *)data_ptr) + block->value_len;
+ }
+
+ return(data_ptr);
+}
+
+/* Returns a pointer to the byte following the Stringtable block, or
+ * NULL if the data read hits padding. We can't recover from this
+ * because the data length does not include padding bytes, so it's not
+ * possible to just return the start position + length
+ */
+static gconstpointer big_up_stringtable_block (gconstpointer data_ptr,
+ version_data *block)
+{
+ guint16 data_len = block->data_len;
+ guint16 string_len = 36; /* length of the StringFileInfo block */
+ gchar *big_value;
+
+ /* data_ptr is pointing at an array of StringTable blocks,
+ * with total length (not including alignment padding) of
+ * data_len
+ */
+
+ while(string_len < data_len) {
+ /* align on a 32-bit boundary */
+ ALIGN32 (data_ptr);
+
+ data_ptr = get_versioninfo_block (data_ptr, block);
+ if (block->data_len == 0) {
+ /* We must have hit padding, so give up
+ * processing now
+ */
+#ifdef DEBUG
+ g_message ("%s: Hit 0-length block, giving up",
+ __func__);
+#endif
+ return(NULL);
+ }
+
+ string_len = string_len + block->data_len;
+
+ big_value = g_convert ((gchar *)block->key, 16, "UTF-16BE",
+ "UTF-16LE", NULL, NULL, NULL);
+ if (big_value == NULL) {
+#ifdef DEBUG
+ g_message ("%s: Didn't find a valid string, giving up",
+ __func__);
+#endif
+ return(NULL);
+ }
+
+ memcpy (block->key, big_value, 16);
+ g_free (big_value);
+
+ data_ptr = big_up_string_block (data_ptr, block);
+
+ if (data_ptr == NULL) {
+ /* Child block hit padding */
+#ifdef DEBUG
+ g_message ("%s: Child block hit 0-length block, giving up", __func__);
+#endif
+ return(NULL);
+ }
+ }
+
+ return(data_ptr);
+}
+
+/* Follows the data structures and turns all UTF-16 strings from the
+ * LE found in the resource section into UTF-16BE
+ */
+static void big_up (gconstpointer datablock, guint32 size)
+{
+ gconstpointer data_ptr;
+ gint32 data_len; /* signed to guard against underflow */
+ version_data block;
+
+ data_ptr = get_fixedfileinfo_block (datablock, &block);
+ if (data_ptr != NULL) {
+ WapiFixedFileInfo *ffi = (WapiFixedFileInfo *)data_ptr;
+
+ /* Byteswap all the fields */
+ ffi->dwFileVersionMS = GUINT32_SWAP_LE_BE (ffi->dwFileVersionMS);
+ ffi->dwFileVersionLS = GUINT32_SWAP_LE_BE (ffi->dwFileVersionLS);
+ ffi->dwProductVersionMS = GUINT32_SWAP_LE_BE (ffi->dwProductVersionMS);
+ ffi->dwProductVersionLS = GUINT32_SWAP_LE_BE (ffi->dwProductVersionLS);
+ ffi->dwFileFlagsMask = GUINT32_SWAP_LE_BE (ffi->dwFileFlagsMask);
+ ffi->dwFileFlags = GUINT32_SWAP_LE_BE (ffi->dwFileFlags);
+ ffi->dwFileOS = GUINT32_SWAP_LE_BE (ffi->dwFileOS);
+ ffi->dwFileType = GUINT32_SWAP_LE_BE (ffi->dwFileType);
+ ffi->dwFileSubtype = GUINT32_SWAP_LE_BE (ffi->dwFileSubtype);
+ ffi->dwFileDateMS = GUINT32_SWAP_LE_BE (ffi->dwFileDateMS);
+ ffi->dwFileDateLS = GUINT32_SWAP_LE_BE (ffi->dwFileDateLS);
+
+ /* The FFI and header occupies the first 92 bytes
+ */
+ data_ptr = (char *)data_ptr + sizeof(WapiFixedFileInfo);
+ data_len = block.data_len - 92;
+
+ /* There now follow zero or one StringFileInfo blocks
+ * and zero or one VarFileInfo blocks
+ */
+ while (data_len > 0) {
+ /* align on a 32-bit boundary */
+ ALIGN32 (data_ptr);
+
+ data_ptr = get_versioninfo_block (data_ptr, &block);
+ if (block.data_len == 0) {
+ /* We must have hit padding, so give
+ * up processing now
+ */
+#ifdef DEBUG
+ g_message ("%s: Hit 0-length block, giving up",
+ __func__);
+#endif
+ return;
+ }
+
+ data_len = data_len - block.data_len;
+
+ if (unicode_string_equals (block.key, "VarFileInfo")) {
+ data_ptr = get_varfileinfo_block (data_ptr,
+ &block);
+ data_ptr = ((guchar *)data_ptr) + block.value_len;
+ } else if (unicode_string_equals (block.key,
+ "StringFileInfo")) {
+ data_ptr = big_up_stringtable_block (data_ptr,
+ &block);
+ } else {
+ /* Bogus data */
+#ifdef DEBUG
+ g_message ("%s: Not a valid VERSIONINFO child block", __func__);
+#endif
+ return;
+ }
+
+ if (data_ptr == NULL) {
+ /* Child block hit padding */
+#ifdef DEBUG
+ g_message ("%s: Child block hit 0-length block, giving up", __func__);
+#endif
+ return;
+ }
+ }
+ }
+}
+#endif
+
gboolean VerQueryValue (gconstpointer datablock, const gunichar2 *subblock,
gpointer *buffer, guint32 *len)
{
*/
while (data_len > 0) {
/* align on a 32-bit boundary */
- data_ptr = (gpointer)((char *)data_ptr + 3);
- data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3));
+ ALIGN32 (data_ptr);
data_ptr = get_versioninfo_block (data_ptr,
&block);
string_value != NULL &&
string_value_len != 0) {
*buffer = string_value;
- *len = string_value_len / 2; /* chars */
+ *len = unicode_chars (string_value) + 1; /* Include trailing null */
ret = TRUE;
goto done;
}
return(ret);
}
+guint32 GetFileVersionInfoSize (gunichar2 *filename, guint32 *handle)
+{
+ gpointer file_map;
+ gpointer versioninfo;
+ guint32 map_size;
+ guint32 size;
+
+ /* This value is unused, but set to zero */
+ *handle = 0;
+
+ file_map = map_pe_file (filename, &map_size);
+ if (file_map == NULL) {
+ return(0);
+ }
+
+ versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION,
+ 0, &size);
+ if (versioninfo == NULL) {
+ /* Didn't find the resource, so set the return value
+ * to 0
+ */
+ size = 0;
+ }
+
+ unmap_pe_file (file_map, map_size);
+
+ return(size);
+}
+
+gboolean GetFileVersionInfo (gunichar2 *filename, guint32 handle G_GNUC_UNUSED,
+ guint32 len, gpointer data)
+{
+ gpointer file_map;
+ gpointer versioninfo;
+ guint32 map_size;
+ guint32 size;
+ gboolean ret = FALSE;
+
+ file_map = map_pe_file (filename, &map_size);
+ if (file_map == NULL) {
+ return(FALSE);
+ }
+
+ versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION,
+ 0, &size);
+ if (versioninfo != NULL) {
+ /* This could probably process the data so that
+ * VerQueryValue() doesn't have to follow the data
+ * blocks every time. But hey, these functions aren't
+ * likely to appear in many profiles.
+ */
+ memcpy (data, versioninfo, len < size?len:size);
+ ret = TRUE;
+
+#if G_BYTE_ORDER == G_BIG_ENDIAN
+ big_up (data, size);
+#endif
+ }
+
+ unmap_pe_file (file_map, map_size);
+
+ return(ret);
+}
+
+static guint32 copy_lang (gunichar2 *lang_out, guint32 lang_len,
+ const gchar *text)
+{
+ gunichar2 *unitext;
+ int chars = strlen (text);
+ int ret;
+
+ unitext = g_utf8_to_utf16 (text, -1, NULL, NULL, NULL);
+ g_assert (unitext != NULL);
+
+ if (chars < (lang_len - 1)) {
+ memcpy (lang_out, (gpointer)unitext, chars * 2);
+ lang_out[chars] = '\0';
+ ret = chars;
+ } else {
+ memcpy (lang_out, (gpointer)unitext, (lang_len - 1) * 2);
+ lang_out[lang_len] = '\0';
+ ret = lang_len;
+ }
+
+ g_free (unitext);
+
+ return(ret);
+}
+
guint32 VerLanguageName (guint32 lang, gunichar2 *lang_out, guint32 lang_len)
{
- return(0);
+ int primary, secondary;
+
+ primary = lang & 0x3FF;
+ secondary = (lang >> 10) & 0x3F;
+
+ switch(primary) {
+ case 0x00:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Process Default Language"));
+ break;
+ }
+ break;
+ case 0x01:
+ switch(secondary) {
+ case 0x00:
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Arabic (Saudi Arabia)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Arabic (Iraq)"));
+ break;
+ case 0x03:
+ return(copy_lang (lang_out, lang_len, "Arabic (Egypt)"));
+ break;
+ case 0x04:
+ return(copy_lang (lang_out, lang_len, "Arabic (Libya)"));
+ break;
+ case 0x05:
+ return(copy_lang (lang_out, lang_len, "Arabic (Algeria)"));
+ break;
+ case 0x06:
+ return(copy_lang (lang_out, lang_len, "Arabic (Morocco)"));
+ break;
+ case 0x07:
+ return(copy_lang (lang_out, lang_len, "Arabic (Tunisia)"));
+ break;
+ case 0x08:
+ return(copy_lang (lang_out, lang_len, "Arabic (Oman)"));
+ break;
+ case 0x09:
+ return(copy_lang (lang_out, lang_len, "Arabic (Yemen)"));
+ break;
+ case 0x0a:
+ return(copy_lang (lang_out, lang_len, "Arabic (Syria)"));
+ break;
+ case 0x0b:
+ return(copy_lang (lang_out, lang_len, "Arabic (Jordan)"));
+ break;
+ case 0x0c:
+ return(copy_lang (lang_out, lang_len, "Arabic (Lebanon)"));
+ break;
+ case 0x0d:
+ return(copy_lang (lang_out, lang_len, "Arabic (Kuwait)"));
+ break;
+ case 0x0e:
+ return(copy_lang (lang_out, lang_len, "Arabic (U.A.E.)"));
+ break;
+ case 0x0f:
+ return(copy_lang (lang_out, lang_len, "Arabic (Bahrain)"));
+ break;
+ case 0x10:
+ return(copy_lang (lang_out, lang_len, "Arabic (Qatar)"));
+ break;
+ }
+ break;
+ case 0x02:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Bulgarian (Bulgaria)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Bulgarian"));
+ break;
+ }
+ break;
+ case 0x03:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Catalan (Spain)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Catalan"));
+ break;
+ }
+ break;
+ case 0x04:
+ switch(secondary) {
+ case 0x00:
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Chinese (Taiwan)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Chinese (PRC)"));
+ break;
+ case 0x03:
+ return(copy_lang (lang_out, lang_len, "Chinese (Hong Kong S.A.R.)"));
+ break;
+ case 0x04:
+ return(copy_lang (lang_out, lang_len, "Chinese (Singapore)"));
+ break;
+ case 0x05:
+ return(copy_lang (lang_out, lang_len, "Chinese (Macau S.A.R.)"));
+ break;
+ }
+ break;
+ case 0x05:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Czech (Czech Republic)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Czech"));
+ break;
+ }
+ break;
+ case 0x06:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Danish (Denmark)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Danish"));
+ break;
+ }
+ break;
+ case 0x07:
+ switch(secondary) {
+ case 0x00:
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "German (Germany)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "German (Switzerland)"));
+ break;
+ case 0x03:
+ return(copy_lang (lang_out, lang_len, "German (Austria)"));
+ break;
+ case 0x04:
+ return(copy_lang (lang_out, lang_len, "German (Luxembourg)"));
+ break;
+ case 0x05:
+ return(copy_lang (lang_out, lang_len, "German (Liechtenstein)"));
+ break;
+ }
+ break;
+ case 0x08:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Greek (Greece)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Greek"));
+ break;
+ }
+ break;
+ case 0x09:
+ switch(secondary) {
+ case 0x00:
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "English (United States)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "English (United Kingdom)"));
+ break;
+ case 0x03:
+ return(copy_lang (lang_out, lang_len, "English (Australia)"));
+ break;
+ case 0x04:
+ return(copy_lang (lang_out, lang_len, "English (Canada)"));
+ break;
+ case 0x05:
+ return(copy_lang (lang_out, lang_len, "English (New Zealand)"));
+ break;
+ case 0x06:
+ return(copy_lang (lang_out, lang_len, "English (Ireland)"));
+ break;
+ case 0x07:
+ return(copy_lang (lang_out, lang_len, "English (South Africa)"));
+ break;
+ case 0x08:
+ return(copy_lang (lang_out, lang_len, "English (Jamaica)"));
+ break;
+ case 0x09:
+ return(copy_lang (lang_out, lang_len, "English (Caribbean)"));
+ break;
+ case 0x0a:
+ return(copy_lang (lang_out, lang_len, "English (Belize)"));
+ break;
+ case 0x0b:
+ return(copy_lang (lang_out, lang_len, "English (Trinidad and Tobago)"));
+ break;
+ case 0x0c:
+ return(copy_lang (lang_out, lang_len, "English (Zimbabwe)"));
+ break;
+ case 0x0d:
+ return(copy_lang (lang_out, lang_len, "English (Philippines)"));
+ break;
+ case 0x10:
+ return(copy_lang (lang_out, lang_len, "English (India)"));
+ break;
+ case 0x11:
+ return(copy_lang (lang_out, lang_len, "English (Malaysia)"));
+ break;
+ case 0x12:
+ return(copy_lang (lang_out, lang_len, "English (Singapore)"));
+ break;
+ }
+ break;
+ case 0x0a:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Spanish (Spain)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Spanish (Traditional Sort)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Spanish (Mexico)"));
+ break;
+ case 0x03:
+ return(copy_lang (lang_out, lang_len, "Spanish (International Sort)"));
+ break;
+ case 0x04:
+ return(copy_lang (lang_out, lang_len, "Spanish (Guatemala)"));
+ break;
+ case 0x05:
+ return(copy_lang (lang_out, lang_len, "Spanish (Costa Rica)"));
+ break;
+ case 0x06:
+ return(copy_lang (lang_out, lang_len, "Spanish (Panama)"));
+ break;
+ case 0x07:
+ return(copy_lang (lang_out, lang_len, "Spanish (Dominican Republic)"));
+ break;
+ case 0x08:
+ return(copy_lang (lang_out, lang_len, "Spanish (Venezuela)"));
+ break;
+ case 0x09:
+ return(copy_lang (lang_out, lang_len, "Spanish (Colombia)"));
+ break;
+ case 0x0a:
+ return(copy_lang (lang_out, lang_len, "Spanish (Peru)"));
+ break;
+ case 0x0b:
+ return(copy_lang (lang_out, lang_len, "Spanish (Argentina)"));
+ break;
+ case 0x0c:
+ return(copy_lang (lang_out, lang_len, "Spanish (Ecuador)"));
+ break;
+ case 0x0d:
+ return(copy_lang (lang_out, lang_len, "Spanish (Chile)"));
+ break;
+ case 0x0e:
+ return(copy_lang (lang_out, lang_len, "Spanish (Uruguay)"));
+ break;
+ case 0x0f:
+ return(copy_lang (lang_out, lang_len, "Spanish (Paraguay)"));
+ break;
+ case 0x10:
+ return(copy_lang (lang_out, lang_len, "Spanish (Bolivia)"));
+ break;
+ case 0x11:
+ return(copy_lang (lang_out, lang_len, "Spanish (El Salvador)"));
+ break;
+ case 0x12:
+ return(copy_lang (lang_out, lang_len, "Spanish (Honduras)"));
+ break;
+ case 0x13:
+ return(copy_lang (lang_out, lang_len, "Spanish (Nicaragua)"));
+ break;
+ case 0x14:
+ return(copy_lang (lang_out, lang_len, "Spanish (Puerto Rico)"));
+ break;
+ case 0x15:
+ return(copy_lang (lang_out, lang_len, "Spanish (United States)"));
+ break;
+ }
+ break;
+ case 0x0b:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Finnish (Finland)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Finnish"));
+ break;
+ }
+ break;
+ case 0x0c:
+ switch(secondary) {
+ case 0x00:
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "French (France)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "French (Belgium)"));
+ break;
+ case 0x03:
+ return(copy_lang (lang_out, lang_len, "French (Canada)"));
+ break;
+ case 0x04:
+ return(copy_lang (lang_out, lang_len, "French (Switzerland)"));
+ break;
+ case 0x05:
+ return(copy_lang (lang_out, lang_len, "French (Luxembourg)"));
+ break;
+ case 0x06:
+ return(copy_lang (lang_out, lang_len, "French (Monaco)"));
+ break;
+ }
+ break;
+ case 0x0d:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Hebrew (Israel)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Hebrew"));
+ break;
+ }
+ break;
+ case 0x0e:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Hungarian (Hungary)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Hungarian"));
+ break;
+ }
+ break;
+ case 0x0f:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Icelandic (Iceland)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Icelandic"));
+ break;
+ }
+ break;
+ case 0x10:
+ switch(secondary) {
+ case 0x00:
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Italian (Italy)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Italian (Switzerland)"));
+ break;
+ }
+ break;
+ case 0x11:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Japanese (Japan)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Japanese"));
+ break;
+ }
+ break;
+ case 0x12:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Korean (Korea)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Korean"));
+ break;
+ }
+ break;
+ case 0x13:
+ switch(secondary) {
+ case 0x00:
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Dutch (Netherlands)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Dutch (Belgium)"));
+ break;
+ }
+ break;
+ case 0x14:
+ switch(secondary) {
+ case 0x00:
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Norwegian (Bokmal)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Norwegian (Nynorsk)"));
+ break;
+ }
+ break;
+ case 0x15:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Polish (Poland)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Polish"));
+ break;
+ }
+ break;
+ case 0x16:
+ switch(secondary) {
+ case 0x00:
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Portuguese (Brazil)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Portuguese (Portugal)"));
+ break;
+ }
+ break;
+ case 0x17:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Romansh (Switzerland)"));
+ break;
+ }
+ break;
+ case 0x18:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Romanian (Romania)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Romanian"));
+ break;
+ }
+ break;
+ case 0x19:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Russian (Russia)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Russian"));
+ break;
+ }
+ break;
+ case 0x1a:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Croatian (Croatia)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Croatian"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Serbian (Latin)"));
+ break;
+ case 0x03:
+ return(copy_lang (lang_out, lang_len, "Serbian (Cyrillic)"));
+ break;
+ case 0x04:
+ return(copy_lang (lang_out, lang_len, "Croatian (Bosnia and Herzegovina)"));
+ break;
+ case 0x05:
+ return(copy_lang (lang_out, lang_len, "Bosnian (Latin, Bosnia and Herzegovina)"));
+ break;
+ case 0x06:
+ return(copy_lang (lang_out, lang_len, "Serbian (Latin, Bosnia and Herzegovina)"));
+ break;
+ case 0x07:
+ return(copy_lang (lang_out, lang_len, "Serbian (Cyrillic, Bosnia and Herzegovina)"));
+ break;
+ case 0x08:
+ return(copy_lang (lang_out, lang_len, "Bosnian (Cyrillic, Bosnia and Herzegovina)"));
+ break;
+ }
+ break;
+ case 0x1b:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Slovak (Slovakia)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Slovak"));
+ break;
+ }
+ break;
+ case 0x1c:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Albanian (Albania)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Albanian"));
+ break;
+ }
+ break;
+ case 0x1d:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Swedish (Sweden)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Swedish"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Swedish (Finland)"));
+ break;
+ }
+ break;
+ case 0x1e:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Thai (Thailand)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Thai"));
+ break;
+ }
+ break;
+ case 0x1f:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Turkish (Turkey)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Turkish"));
+ break;
+ }
+ break;
+ case 0x20:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Urdu (Islamic Republic of Pakistan)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Urdu"));
+ break;
+ }
+ break;
+ case 0x21:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Indonesian (Indonesia)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Indonesian"));
+ break;
+ }
+ break;
+ case 0x22:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Ukrainian (Ukraine)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Ukrainian"));
+ break;
+ }
+ break;
+ case 0x23:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Belarusian (Belarus)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Belarusian"));
+ break;
+ }
+ break;
+ case 0x24:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Slovenian (Slovenia)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Slovenian"));
+ break;
+ }
+ break;
+ case 0x25:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Estonian (Estonia)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Estonian"));
+ break;
+ }
+ break;
+ case 0x26:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Latvian (Latvia)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Latvian"));
+ break;
+ }
+ break;
+ case 0x27:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Lithuanian (Lithuania)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Lithuanian"));
+ break;
+ }
+ break;
+ case 0x28:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Tajik (Tajikistan)"));
+ break;
+ }
+ break;
+ case 0x29:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Farsi (Iran)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Farsi"));
+ break;
+ }
+ break;
+ case 0x2a:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Vietnamese (Viet Nam)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Vietnamese"));
+ break;
+ }
+ break;
+ case 0x2b:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Armenian (Armenia)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Armenian"));
+ break;
+ }
+ break;
+ case 0x2c:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Azeri (Latin) (Azerbaijan)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Azeri (Latin)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Azeri (Cyrillic)"));
+ break;
+ }
+ break;
+ case 0x2d:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Basque (Spain)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Basque"));
+ break;
+ }
+ break;
+ case 0x2e:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Upper Sorbian (Germany)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Lower Sorbian (Germany)"));
+ break;
+ }
+ break;
+ case 0x2f:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "FYRO Macedonian (Former Yugoslav Republic of Macedonia)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "FYRO Macedonian"));
+ break;
+ }
+ break;
+ case 0x32:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Tswana (South Africa)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Tswana"));
+ break;
+ }
+ break;
+ case 0x34:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Xhosa (South Africa)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Xhosa"));
+ break;
+ }
+ break;
+ case 0x35:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Zulu (South Africa)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Zulu"));
+ break;
+ }
+ break;
+ case 0x36:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Afrikaans (South Africa)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Afrikaans"));
+ break;
+ }
+ break;
+ case 0x37:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Georgian (Georgia)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Georgian"));
+ break;
+ }
+ break;
+ case 0x38:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Faroese (Faroe Islands)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Faroese"));
+ break;
+ }
+ break;
+ case 0x39:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Hindi (India)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Hindi"));
+ break;
+ }
+ break;
+ case 0x3a:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Maltese (Malta)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Maltese"));
+ break;
+ }
+ break;
+ case 0x3b:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Sami (Northern) (Norway)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Sami, Northern (Norway)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Sami, Northern (Sweden)"));
+ break;
+ case 0x03:
+ return(copy_lang (lang_out, lang_len, "Sami, Northern (Finland)"));
+ break;
+ case 0x04:
+ return(copy_lang (lang_out, lang_len, "Sami, Lule (Norway)"));
+ break;
+ case 0x05:
+ return(copy_lang (lang_out, lang_len, "Sami, Lule (Sweden)"));
+ break;
+ case 0x06:
+ return(copy_lang (lang_out, lang_len, "Sami, Southern (Norway)"));
+ break;
+ case 0x07:
+ return(copy_lang (lang_out, lang_len, "Sami, Southern (Sweden)"));
+ break;
+ case 0x08:
+ return(copy_lang (lang_out, lang_len, "Sami, Skolt (Finland)"));
+ break;
+ case 0x09:
+ return(copy_lang (lang_out, lang_len, "Sami, Inari (Finland)"));
+ break;
+ }
+ break;
+ case 0x3c:
+ switch(secondary) {
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Irish (Ireland)"));
+ break;
+ }
+ break;
+ case 0x3e:
+ switch(secondary) {
+ case 0x00:
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Malay (Malaysia)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Malay (Brunei Darussalam)"));
+ break;
+ }
+ break;
+ case 0x3f:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Kazakh (Kazakhstan)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Kazakh"));
+ break;
+ }
+ break;
+ case 0x40:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Kyrgyz (Kyrgyzstan)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Kyrgyz (Cyrillic)"));
+ break;
+ }
+ break;
+ case 0x41:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Swahili (Kenya)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Swahili"));
+ break;
+ }
+ break;
+ case 0x42:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Turkmen (Turkmenistan)"));
+ break;
+ }
+ break;
+ case 0x43:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Uzbek (Latin) (Uzbekistan)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Uzbek (Latin)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Uzbek (Cyrillic)"));
+ break;
+ }
+ break;
+ case 0x44:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Tatar (Russia)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Tatar"));
+ break;
+ }
+ break;
+ case 0x45:
+ switch(secondary) {
+ case 0x00:
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Bengali (India)"));
+ break;
+ }
+ break;
+ case 0x46:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Punjabi (India)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Punjabi"));
+ break;
+ }
+ break;
+ case 0x47:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Gujarati (India)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Gujarati"));
+ break;
+ }
+ break;
+ case 0x49:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Tamil (India)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Tamil"));
+ break;
+ }
+ break;
+ case 0x4a:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Telugu (India)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Telugu"));
+ break;
+ }
+ break;
+ case 0x4b:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Kannada (India)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Kannada"));
+ break;
+ }
+ break;
+ case 0x4c:
+ switch(secondary) {
+ case 0x00:
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Malayalam (India)"));
+ break;
+ }
+ break;
+ case 0x4d:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Assamese (India)"));
+ break;
+ }
+ break;
+ case 0x4e:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Marathi (India)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Marathi"));
+ break;
+ }
+ break;
+ case 0x4f:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Sanskrit (India)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Sanskrit"));
+ break;
+ }
+ break;
+ case 0x50:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Mongolian (Mongolia)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Mongolian (Cyrillic)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Mongolian (PRC)"));
+ break;
+ }
+ break;
+ case 0x51:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Tibetan (PRC)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Tibetan (Bhutan)"));
+ break;
+ }
+ break;
+ case 0x52:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Welsh (United Kingdom)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Welsh"));
+ break;
+ }
+ break;
+ case 0x53:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Khmer (Cambodia)"));
+ break;
+ }
+ break;
+ case 0x54:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Lao (Lao PDR)"));
+ break;
+ }
+ break;
+ case 0x56:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Galician (Spain)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Galician"));
+ break;
+ }
+ break;
+ case 0x57:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Konkani (India)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Konkani"));
+ break;
+ }
+ break;
+ case 0x5a:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Syriac (Syria)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Syriac"));
+ break;
+ }
+ break;
+ case 0x5b:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Sinhala (Sri Lanka)"));
+ break;
+ }
+ break;
+ case 0x5d:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Inuktitut (Syllabics, Canada)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Inuktitut (Latin, Canada)"));
+ break;
+ }
+ break;
+ case 0x5e:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Amharic (Ethiopia)"));
+ break;
+ }
+ break;
+ case 0x5f:
+ switch(secondary) {
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Tamazight (Algeria, Latin)"));
+ break;
+ }
+ break;
+ case 0x61:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Nepali (Nepal)"));
+ break;
+ }
+ break;
+ case 0x62:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Frisian (Netherlands)"));
+ break;
+ }
+ break;
+ case 0x63:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Pashto (Afghanistan)"));
+ break;
+ }
+ break;
+ case 0x64:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Filipino (Philippines)"));
+ break;
+ }
+ break;
+ case 0x65:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Divehi (Maldives)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Divehi"));
+ break;
+ }
+ break;
+ case 0x68:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Hausa (Nigeria, Latin)"));
+ break;
+ }
+ break;
+ case 0x6a:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Yoruba (Nigeria)"));
+ break;
+ }
+ break;
+ case 0x6b:
+ switch(secondary) {
+ case 0x00:
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Quechua (Bolivia)"));
+ break;
+ case 0x02:
+ return(copy_lang (lang_out, lang_len, "Quechua (Ecuador)"));
+ break;
+ case 0x03:
+ return(copy_lang (lang_out, lang_len, "Quechua (Peru)"));
+ break;
+ }
+ break;
+ case 0x6c:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Northern Sotho (South Africa)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Northern Sotho"));
+ break;
+ }
+ break;
+ case 0x6d:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Bashkir (Russia)"));
+ break;
+ }
+ break;
+ case 0x6e:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Luxembourgish (Luxembourg)"));
+ break;
+ }
+ break;
+ case 0x6f:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Greenlandic (Greenland)"));
+ break;
+ }
+ break;
+ case 0x78:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Yi (PRC)"));
+ break;
+ }
+ break;
+ case 0x7a:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Mapudungun (Chile)"));
+ break;
+ }
+ break;
+ case 0x7c:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Mohawk (Mohawk)"));
+ break;
+ }
+ break;
+ case 0x7e:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Breton (France)"));
+ break;
+ }
+ break;
+ case 0x7f:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Invariant Language (Invariant Country)"));
+ break;
+ }
+ break;
+ case 0x80:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Uighur (PRC)"));
+ break;
+ }
+ break;
+ case 0x81:
+ switch(secondary) {
+ case 0x00:
+ return(copy_lang (lang_out, lang_len, "Maori (New Zealand)"));
+ break;
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Maori"));
+ break;
+ }
+ break;
+ case 0x83:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Corsican (France)"));
+ break;
+ }
+ break;
+ case 0x84:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Alsatian (France)"));
+ break;
+ }
+ break;
+ case 0x85:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Yakut (Russia)"));
+ break;
+ }
+ break;
+ case 0x86:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "K'iche (Guatemala)"));
+ break;
+ }
+ break;
+ case 0x87:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Kinyarwanda (Rwanda)"));
+ break;
+ }
+ break;
+ case 0x88:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Wolof (Senegal)"));
+ break;
+ }
+ break;
+ case 0x8c:
+ switch(secondary) {
+ case 0x01:
+ return(copy_lang (lang_out, lang_len, "Dari (Afghanistan)"));
+ break;
+ }
+ break;
+
+ default:
+ return(copy_lang (lang_out, lang_len, "Language Neutral"));
+
+ }
+
+ return(copy_lang (lang_out, lang_len, "Language Neutral"));
}