This repository has been archived on 2025-05-24. You can view files and clone it, but cannot push or open issues or pull requests.
Files
VARCem/src/win/win_lang.c
2018-10-06 18:31:10 -04:00

301 lines
8.3 KiB
C

/*
* VARCem Virtual ARchaeological Computer EMulator.
* An emulator of (mostly) x86-based PC systems and devices,
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
* spanning the era between 1981 and 1995.
*
* This file is part of the VARCem Project.
*
* Handle language support for the platform.
*
* Version: @(#)win_lang.c 1.0.6 2018/10/05
*
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
*
* Copyright 2018 Fred N. van Kempen.
*
* Redistribution and use in source and binary forms, with
* or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the entire
* above notice, this list of conditions and the following
* disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define UNICODE
#include <windows.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "../emu.h"
#include "../ui/ui.h"
#include "../plat.h"
#include "win.h"
#include "win_opendir.h"
/* Define a translation entry in the file. */
typedef struct {
WORD wLanguage;
WORD wCodePage;
} dll_lang_t;
/* Local data. */
static HMODULE lang_handle = NULL;
static void
lang_add(lang_t *ptr, int sort)
{
wchar_t name[128];
wchar_t *str;
LCID lcid;
/* Convert the language ID to a (localized) name. */
lcid = MAKELCID(ptr->id, SORT_DEFAULT);
if (! GetLocaleInfo(lcid, LOCALE_SLANGUAGE, name, sizeof_w(name))) {
ERRLOG("UI: unable to get name for language ID 0x%04X\n", ptr->id);
return;
}
/* Clean up any existing name. */
if (ptr->name != NULL)
free((void *)ptr->name);
/* Set the (new) name. */
str = (wchar_t *)mem_alloc(sizeof(wchar_t) * (wcslen(name) + 1));
memset(str, 0x00, sizeof(wchar_t) * (wcslen(name) + 1));
wcscpy(str, name);
ptr->name = (const wchar_t *)str;
/* Actually add this language. */
ui_lang_add(ptr, sort);
}
/*
* Scan the LANG dir for any Language Modules we
* may have installed there. The main application
* only has English compiled in, any others are
* kept in separate language modules.
*/
void
plat_lang_scan(void)
{
wchar_t path[512], temp[512];
uint8_t buffer[8192];
wchar_t *str, *ptr;
struct direct *de;
dll_lang_t *lptr;
lang_t lang;
DIR *dir;
int l, lflen;
lflen = wcslen(LANG_FILE);
/* Open the "language modules" directory. */
swprintf(path, sizeof_w(temp), L"%ls%ls", exe_path, LANGUAGE_PATH);
dir = opendir(path);
if (dir != NULL) {
/* Scan all files, and see if we find a usable one. */
for (;;) {
/* Get an entry from the directory. */
if ((de = readdir(dir)) == NULL) break;
/* Is this a file we want? */
if ((int)wcslen(de->d_name) < lflen) continue;
if (wcsncmp(de->d_name, LANG_FILE, lflen)) continue;
str = wcschr(de->d_name, L'.');
if (str == NULL) continue;
if (wcsncasecmp(str, L".dll", 4)) continue;
/* Looks like we have one here.. */
memset(temp, 0x00, sizeof(temp));
swprintf(temp, sizeof_w(temp), L"%ls\\%ls", path, de->d_name);
l = wcslen(temp) + 1;
/* Create a buffer for the DLL file name. */
memset(&lang, 0x00, sizeof(lang));
str = (wchar_t *)mem_alloc(sizeof(wchar_t) * l);
memset(str, 0x00, sizeof(wchar_t) * l);
wcscpy(str, temp);
lang.dll = (const wchar_t *)str;
/* Grab the version info block from the DLL. */
if (! GetFileVersionInfo(temp, 0, sizeof(buffer), buffer)) {
ERRLOG("UI: unable to access '%ls', skipping!\n", temp);
free((void *)lang.dll);
continue;
}
/* Extract the (first) "Translation" entry. */
VerQueryValue(buffer, L"\\VarFileInfo\\Translation",
(LPVOID*)&lptr, (PUINT)&l);
lang.id = lptr->wLanguage;
/* Get the various strings from the block. */
swprintf(temp, sizeof_w(temp),
L"\\StringFileInfo\\%04x%04x\\%ls",
lptr->wLanguage, lptr->wCodePage, L"FileVersion");
if (! VerQueryValue(buffer, temp, (LPVOID*)&str, (PUINT)&l)) {
ERRLOG("UI: invalid data in DLL, skipping!\n");
free((void *)lang.dll);
continue;
}
ptr = (wchar_t *)mem_alloc(sizeof(wchar_t) * (l+1));
lang.version = (const wchar_t *)ptr;
*ptr++ = L'v';
do {
/* Copy string, replacing , with . on the fly. */
if (*str == L',')
*ptr++ = L'.';
else
*ptr++ = *str;
} while (*str++ != L'\0');
swprintf(temp, sizeof_w(temp),
L"\\StringFileInfo\\%04x%04x\\%ls",
lptr->wLanguage, lptr->wCodePage, L"Comments");
if (! VerQueryValue(buffer, temp, (LPVOID*)&str, (PUINT)&l)) {
ERRLOG("UI: invalid data in DLL, skipping!\n");
free((void *)lang.version);
free((void *)lang.dll);
continue;
}
ptr = temp;
while (*str != L'\0')
*ptr++ = *str++;
*ptr++ = *str++;
l -= (ptr - temp);
ptr = (wchar_t *)mem_alloc(sizeof(wchar_t) * (ptr-temp));
wcscpy(ptr, temp);
lang.author = (const wchar_t *)ptr;
ptr = (wchar_t *)mem_alloc(sizeof(wchar_t) * l);
wcscpy(ptr, str);
lang.email = (const wchar_t *)ptr;
/* Add this language. */
lang_add(&lang, 1);
}
(void)closedir(dir);
}
/* Add the application's primary language. */
memset(&lang, 0x00, sizeof(lang));
lang.id = 0x0409;
lang_add(&lang, 0);
}
/* Activate the new language for the application. */
void
plat_lang_set(int id)
{
LCID lcid;
SetThreadUILanguage(id);
/* This can be removed, it no longer works since Vista+ */
lcid = MAKELCID(id, SORT_DEFAULT);
SetThreadLocale(lcid);
}
/* Pre-load the strings from our resource file. */
const string_t *
plat_lang_load(lang_t *ptr)
{
wchar_t temp[512], *str;
string_t *array, *tbl;
int c = 0, i;
/* Do we need to unload a resource DLL? */
if (lang_handle != NULL) {
FreeLibrary(lang_handle);
lang_handle = NULL;
}
/* Do we need to load a resource DLL? */
if (ptr->dll != NULL) {
lang_handle = LoadLibraryEx(ptr->dll, 0,
LOAD_LIBRARY_AS_IMAGE_RESOURCE | \
LOAD_LIBRARY_AS_DATAFILE);
if (lang_handle == NULL) {
ERRLOG("UI: unable to load resource DLL '%ls' !\n", ptr->dll);
return(NULL);
}
}
/*
* First, we need to know how many strings are in the table.
* Sadly, there is no other way to determine this but to try
* to load all possible ID's...
*/
for (i = IDS_BEGIN; i < IDS_END; i++)
if (LoadString(plat_lang_dll(), i, temp, sizeof_w(temp)) > 0) c++;
/*
* Now that we know how many strings exist, we can allocate
* our string_table array.
*/
i = (c + 1) * sizeof(string_t);
array = (string_t *)mem_alloc(i);
memset(array, 0x00, i);
/* Now load the actual strings into our string table. */
tbl = array;
for (i = IDS_BEGIN; i < IDS_END; i++) {
c = LoadString(plat_lang_dll(), i, temp, sizeof_w(temp));
if (c == 0) continue;
tbl->id = i;
str = (wchar_t *)mem_alloc((c + 1) * sizeof(wchar_t));
memset(str, 0x00, (c + 1) * sizeof(wchar_t));
memcpy(str, temp, c * sizeof(wchar_t));
tbl->str = str;
tbl++;
}
/* Terminate the table. */
tbl->str = NULL;
return((const string_t *)array);
}
HMODULE
plat_lang_dll(void)
{
if (lang_handle == NULL)
return(hInstance);
return(lang_handle);
}