/*
 * 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.8	2018/10/24
 *
 * 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 = (int)wcslen(LANG_FILE);

    /* Open the "language modules" directory. */
    swprintf(path, sizeof_w(path), 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 = (int)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 -= (int)(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);
}
