mirror of
https://github.com/WinampDesktop/winamp.git
synced 2024-11-28 00:30:50 +01:00
1136 lines
34 KiB
C++
1136 lines
34 KiB
C++
/** (c) Nullsoft, Inc. C O N F I D E N T I A L
|
|
** Filename:
|
|
** Project:
|
|
** Description: Utility functions for handling language support
|
|
** Author:
|
|
** Created:
|
|
**/
|
|
|
|
#include <locale.h>
|
|
#include "main.h"
|
|
#include "language.h"
|
|
#include "../nu/AutoWide.h"
|
|
#include "../nu/AutoChar.h"
|
|
#include "minizip/unzip.h"
|
|
#include <vector>
|
|
#include "../nu/AutoCharFn.h"
|
|
|
|
typedef struct {
|
|
wchar_t *module;
|
|
wchar_t *guidstr; // generally is 0 or 39 (38+null)
|
|
int guid;
|
|
int external;
|
|
HINSTANCE hDllInstance;
|
|
} winampLangStruct;
|
|
|
|
static std::vector<winampLangStruct*> lnglist;
|
|
int already_extracted = 0, prev_wlz_ex_state = 0, geno = 1, started = 0;
|
|
|
|
// data storage of the read in values requires just the hash and the id to be stored
|
|
// and has to be logged against the different id types so just need a list of structs
|
|
// based against each type (dialog, string resource, etc)
|
|
|
|
// for the moment we deal with the following resource types
|
|
// RT_DIALOG, RT_MENU, RT_STRING & custom resources
|
|
// so for initial implementation we only need to store upto 4 hash+id lists
|
|
|
|
#if 0
|
|
typedef struct {
|
|
int id;
|
|
char id_str[32];
|
|
char hash[17];
|
|
char str[64];
|
|
} hashstruct;
|
|
|
|
std::vector<hashstruct*> dialogList;
|
|
std::vector<hashstruct*> menuList;
|
|
std::vector<hashstruct*> stringList; // will be the largest of the lot
|
|
std::vector<hashstruct*> customList; // should be very few of this
|
|
#endif
|
|
|
|
// have section header (text or int id)
|
|
// then the hash and then the id that's related the hash eg the dialog resource id
|
|
|
|
void ReadHashFileDetails(char* data, DWORD datalen)
|
|
{
|
|
#if 0
|
|
char* p = data, *s = p, *t = 0, *u;
|
|
while(s && *s)
|
|
{
|
|
// is it the start of a block that we've just gotten to...
|
|
if(*s == '@' || *s == '#')
|
|
{
|
|
int id = -1;
|
|
char id_str[32] = {0};
|
|
u = s = CharNext(s);
|
|
if(!*s){break;}
|
|
|
|
// advance to the end of the line to get the block identifier
|
|
// would need to use the @ or # to process the type used
|
|
// ie if a type 5 then only use on dialog loading calls
|
|
while(u && *u && *u != '\n'){u = CharNext(u);}
|
|
if(*u == '\n'){u = CharNext(u);*CharPrev(p,u) = 0;}
|
|
if(!*u){break;}
|
|
|
|
// identifier of the block is found here :)
|
|
if(*s)
|
|
{
|
|
id = atoi(s);
|
|
if(!id)
|
|
{
|
|
lstrcpyn(id_str, s, sizeof(id_str));
|
|
}
|
|
}
|
|
*CharPrev(p,u) = '\n';
|
|
|
|
while(s && *s && (*s != '@' && *s != '#'))
|
|
{
|
|
int end = 0;
|
|
|
|
while(s && *s && *s != '\n'){s = CharNext(s);}
|
|
if(*s == '\n'){s = CharNext(s);}
|
|
// if nothing else then need to abort (since we don't want to do bad things)
|
|
// and have to take into account where in the buffer we are otherwise we can
|
|
// end up going into the next part of the dll/exe resource data due to how
|
|
// it is all stored/handled in them (ie butted up against each other)
|
|
if(!*s || s >= p+datalen){break;}
|
|
|
|
t = s;
|
|
// do a check after we've advanced to the start of a new line
|
|
// so that we can see if we've hit a new resource type block
|
|
if(*s == '@' || *s == '#')
|
|
{
|
|
s = CharPrev(p,s);
|
|
break;
|
|
}
|
|
|
|
// scan through to the start of the second part of the <hash:id> block
|
|
while(t && *t && *t != ':')
|
|
{
|
|
t = CharNext(t);
|
|
}
|
|
|
|
if(*t == ':')
|
|
{
|
|
t = CharNext(t);
|
|
*CharPrev(p,t) = 0;
|
|
}
|
|
|
|
// scan through to the end of the line so that we then have the id
|
|
u = t;
|
|
while(u && *u && *u != '\n')
|
|
{
|
|
u = CharNext(u);
|
|
}
|
|
|
|
if(*u == '\n')
|
|
{
|
|
u = CharNext(u);
|
|
*CharPrev(p,u) = 0;
|
|
}
|
|
|
|
// hash and identifier of the entry is found here :)
|
|
// -> need to check how it works with IDD_CRASHDLG$()
|
|
if(*s)
|
|
{
|
|
hashstruct* tempList = reinterpret_cast<hashstruct*>(calloc(1, sizeof(hashstruct)));
|
|
ZeroMemory(tempList,sizeof(hashstruct));
|
|
|
|
/*if(*t == 1) wsprintf(a,"%s %d (%s)\n", s, *t, t+1);
|
|
else wsprintf(a,"%s %s (%d)\n", s, t+1, *t);*/
|
|
if(*t == 1) // int_id
|
|
lstrcpyn(tempList->str, t+1, sizeof(tempList->str));
|
|
else // string_id
|
|
lstrcpyn(tempList->str, t+1, *t/*sizeof(tempList->str)*/);
|
|
|
|
lstrcpyn(tempList->hash, s, sizeof(tempList->hash));
|
|
if(id) tempList->id = id;
|
|
|
|
switch(id)
|
|
{
|
|
case RT_MENU:
|
|
{
|
|
menuList.push_back(tempList);
|
|
}
|
|
break;
|
|
case RT_DIALOG:
|
|
{
|
|
dialogList.push_back(tempList);
|
|
}
|
|
break;
|
|
case RT_STRING:
|
|
{
|
|
stringList.push_back(tempList);
|
|
}
|
|
break;
|
|
default:
|
|
// only do if there's no id from atoi (indicates a custom resource id)
|
|
if(!id)
|
|
{
|
|
lstrcpyn(tempList->id_str, id_str, sizeof(tempList->id_str));
|
|
customList.push_back(tempList);
|
|
}
|
|
break;
|
|
}
|
|
|
|
{
|
|
char zz[100] = {0};
|
|
StringCchPrintf(zz,100,"ID: '%s' %d\t%s %s\n",
|
|
tempList->id_str, tempList->id,
|
|
tempList->hash, tempList->str);
|
|
OutputDebugString(zz);
|
|
}
|
|
}
|
|
*CharPrev(p,u) = '\n';
|
|
s = CharPrev(p,u);
|
|
}
|
|
}
|
|
s = CharNext(s);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int GetImageHashData(HINSTANCE imageInstance)
|
|
{
|
|
DWORD datalen = 0;
|
|
void* data = langManager->LoadResourceFromFile(imageInstance,imageInstance,L"HASH",L"HASH",&datalen);
|
|
ReadHashFileDetails((char*)data,datalen);
|
|
UnlockResource(data);
|
|
FreeResource(data);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
static void CheckLangThread()
|
|
{
|
|
if (mainThreadId != GetCurrentThreadId())
|
|
{
|
|
// DebugBreak();
|
|
/**
|
|
** If you hit this breakpoint, it's because you tried to use WASABI_API_LANG->GetString on another thread,
|
|
** without supplying your own buffer.
|
|
** You really don't want to be doing that.
|
|
** Hit alt+7, check what function is calling GetString/GetStringW and go fix it.
|
|
** Now.
|
|
**/
|
|
}
|
|
}
|
|
|
|
#else
|
|
#define CheckLangThread()
|
|
#endif
|
|
|
|
char *getString(UINT uID, char *str, size_t maxlen)
|
|
{
|
|
return langManager->GetString(language_pack_instance,hMainInstance, uID, str, maxlen);
|
|
}
|
|
|
|
int LPMessageBox(HWND parent, UINT idMessage, UINT idTitle, UINT type)
|
|
{
|
|
wchar_t message[32768] = {0};
|
|
wchar_t title[256] = {0};
|
|
|
|
// TODO consider exposing something in the winamp.lng file so we can follow this where possible as it should allow for a localised messagebox as long as the OS supports the language
|
|
// return MessageBoxExW(parent,getStringW(idMessage,message,32768),getStringW(idTitle,title,256),type,MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH));
|
|
return MessageBoxW(parent,getStringW(idMessage,message,32768),getStringW(idTitle,title,256),type);
|
|
}
|
|
|
|
char* Language::GetString(HINSTANCE hinst, HINSTANCE owner, UINT uID, char *str, size_t maxlen)
|
|
{
|
|
__declspec(thread) static char *buf;
|
|
if (!str)
|
|
{
|
|
CheckLangThread();
|
|
if (!buf)
|
|
buf = (char *)calloc(LANG_STATIC_BUFFER_SIZE, sizeof(buf[0]));
|
|
str = buf;
|
|
maxlen = LANG_STATIC_BUFFER_SIZE;
|
|
}
|
|
|
|
// sometimes we need to ignore things i.e. accessing on closing, etc
|
|
if (((unsigned long)str >= 65536) && !LoadStringA((started ? hinst : owner), uID, str, (int)maxlen))
|
|
{
|
|
if (hinst == owner || !LoadStringA(owner, uID, str, (int)maxlen))
|
|
{
|
|
lstrcpynA(str, "Error loading string", (int)maxlen);
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
wchar_t *getStringW(UINT uID, wchar_t *str, size_t maxlen)
|
|
{
|
|
return langManager->GetStringW(language_pack_instance,hMainInstance, uID, str, maxlen);
|
|
}
|
|
|
|
wchar_t* Language::GetStringW(HINSTANCE hinst, HINSTANCE owner, UINT uID, wchar_t *str, size_t maxlen)
|
|
{
|
|
__declspec(thread) static wchar_t *buf;
|
|
if (!str)
|
|
{
|
|
CheckLangThread();
|
|
if (!buf)
|
|
buf = (wchar_t *)calloc(LANG_STATIC_BUFFER_SIZE, sizeof(buf[0]));
|
|
str = buf;
|
|
maxlen = LANG_STATIC_BUFFER_SIZE;
|
|
}
|
|
|
|
// sometimes we need to ignore things i.e. accessing on closing, etc
|
|
if (((unsigned long)str >= 65536) && !LoadStringW((started ? hinst : owner), uID, str, (int)maxlen))
|
|
{
|
|
if (hinst == owner || !LoadStringW(owner, uID, str, (int)maxlen))
|
|
{
|
|
lstrcpynW(str, L"Error loading string", (int)maxlen);
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
char* Language::GetStringFromGUID(const GUID guid, HINSTANCE owner, UINT uID, char *str, size_t maxlen)
|
|
{
|
|
__declspec(thread) static char *buf;
|
|
if (!str)
|
|
{
|
|
CheckLangThread();
|
|
if (!buf)
|
|
buf = (char *)calloc(LANG_STATIC_BUFFER_SIZE, sizeof(buf[0]));
|
|
str = buf;
|
|
maxlen = LANG_STATIC_BUFFER_SIZE;
|
|
}
|
|
|
|
HINSTANCE tl = FindDllHandleByGUID(guid);
|
|
if(!tl) tl = owner;
|
|
|
|
// sometimes we need to ignore things i.e. accessing on closing, etc
|
|
if (((unsigned long)str >= 65536) && !LoadStringA((started ? tl : owner), uID, str, (int)maxlen))
|
|
{
|
|
if (!LoadStringA(owner, uID, str, (int)maxlen))
|
|
{
|
|
lstrcpynA(str, "Error loading string", (int)maxlen);
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
wchar_t* Language::GetStringFromGUIDW(const GUID guid, HINSTANCE owner, UINT uID, wchar_t *str, size_t maxlen)
|
|
{
|
|
__declspec(thread) static wchar_t *buf;
|
|
if (!str)
|
|
{
|
|
CheckLangThread();
|
|
if (!buf)
|
|
buf = (wchar_t *)calloc(LANG_STATIC_BUFFER_SIZE, sizeof(buf[0]));
|
|
str = buf;
|
|
maxlen = LANG_STATIC_BUFFER_SIZE;
|
|
}
|
|
|
|
HINSTANCE tl = FindDllHandleByGUID(guid);
|
|
if(!tl) tl = owner;
|
|
|
|
// sometimes we need to ignore things i.e. accessing on closing, etc
|
|
if (((unsigned long)str >= 65536) && !LoadStringW((started ? tl : owner), uID, str, (int) maxlen))
|
|
{
|
|
if (!LoadStringW(owner, uID, str, (int) maxlen))
|
|
{
|
|
lstrcpynW(str, L"Error loading string", (int) maxlen);
|
|
}
|
|
}
|
|
return str;
|
|
}
|
|
|
|
const wchar_t *Language::GetLanguageFolder()
|
|
{
|
|
return (LANGTEMPDIR[0] ? LANGTEMPDIR : lang_directory);
|
|
}
|
|
|
|
void* Language::LoadResourceFromFileW(HINSTANCE hinst, HINSTANCE owner, LPCWSTR lpType, LPCWSTR lpName, DWORD* size)
|
|
{
|
|
HINSTANCE hmod = hinst;
|
|
HRSRC rsrc = FindResourceW(hmod, lpName, lpType);
|
|
if(!rsrc)
|
|
{
|
|
hmod = owner;
|
|
rsrc = FindResourceW(hmod, lpName, lpType);
|
|
}
|
|
if(rsrc)
|
|
{
|
|
HGLOBAL resourceHandle = LoadResource(hmod, rsrc);
|
|
if(size){*size = SizeofResource(hmod, rsrc);}
|
|
return LockResource(resourceHandle);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void* Language::LoadResourceFromFile(HINSTANCE hinst, HINSTANCE owner, LPCTSTR lpType, LPCTSTR lpName, DWORD* size)
|
|
{
|
|
HINSTANCE hmod = hinst;
|
|
HRSRC rsrc = FindResource(hmod, lpName, lpType);
|
|
if(!rsrc)
|
|
{
|
|
hmod = owner;
|
|
rsrc = FindResource(hmod, lpName, lpType);
|
|
}
|
|
if(rsrc)
|
|
{
|
|
HGLOBAL resourceHandle = LoadResource(hmod, rsrc);
|
|
if(size){*size = SizeofResource(hmod, rsrc);}
|
|
return LockResource(resourceHandle);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const wchar_t *Language::GetLanguageIdentifier( int mode )
|
|
{
|
|
static wchar_t id_str[ 9 ] = { 0 };
|
|
id_str[ 0 ] = 0;
|
|
// 5.58 fix - was returning en-US on all calls to this via load_extra_lng(..)
|
|
// make sure to try to use a loaded winamp.lng as load_extra_lng(..) relies on
|
|
// this for the path to use but calls it before getStringW(..) will work fully
|
|
GetStringFromGUIDW( WinampLangGUID, hMainInstance, LANG_PACK_LANG_ID, id_str, 9 );
|
|
|
|
if ( !_wcsicmp( id_str, L"Error l" ) )
|
|
{
|
|
id_str[ 0 ] = 0;
|
|
}
|
|
|
|
if ( mode && id_str[ 0 ] )
|
|
{
|
|
wchar_t *iStr = id_str;
|
|
|
|
while ( iStr && *iStr && *iStr != L'-' )
|
|
{
|
|
iStr = CharNextW( iStr );
|
|
}
|
|
|
|
if ( iStr && *iStr == '-' )
|
|
{
|
|
iStr = CharNextW( iStr );
|
|
*CharPrevW( id_str, iStr ) = 0;
|
|
}
|
|
|
|
if ( mode == LANG_LANG_CODE )
|
|
return id_str;
|
|
else if ( mode == LANG_COUNTRY_CODE )
|
|
return iStr;
|
|
}
|
|
|
|
return ( id_str[ 0 ] ? id_str : 0 );
|
|
}
|
|
|
|
HWND Language::CreateLDialogParam( HINSTANCE localised, HINSTANCE original, UINT id, HWND parent, DLGPROC proc, LPARAM param )
|
|
{
|
|
HWND hwnd = (HWND)CreateDialogParamA( localised, MAKEINTRESOURCEA( id ), parent, proc, param );
|
|
if ( !hwnd && localised != original )
|
|
hwnd = (HWND)CreateDialogParamA( original, MAKEINTRESOURCEA( id ), parent, proc, param );
|
|
return hwnd;
|
|
}
|
|
|
|
HWND Language::CreateLDialogParamW( HINSTANCE localised, HINSTANCE original, UINT id, HWND parent, DLGPROC proc, LPARAM param )
|
|
{
|
|
HWND hwnd = (HWND)CreateDialogParamW( localised, MAKEINTRESOURCEW( id ), parent, proc, param );
|
|
if ( !hwnd && localised != original )
|
|
hwnd = (HWND)CreateDialogParamW( original, MAKEINTRESOURCEW( id ), parent, proc, param );
|
|
return hwnd;
|
|
}
|
|
|
|
INT_PTR Language::LDialogBoxParam( HINSTANCE localised, HINSTANCE original, UINT id, HWND parent, DLGPROC proc, LPARAM param )
|
|
{
|
|
INT_PTR ret = DialogBoxParamA( localised, MAKEINTRESOURCEA( id ), parent, proc, param );
|
|
if ( ( ret == -1 && GetLastError() != ERROR_SUCCESS ) && localised != original )
|
|
ret = DialogBoxParamA( original, MAKEINTRESOURCEA( id ), parent, proc, param );
|
|
return ret;
|
|
}
|
|
|
|
INT_PTR Language::LDialogBoxParamW( HINSTANCE localised, HINSTANCE original, UINT id, HWND parent, DLGPROC proc, LPARAM param )
|
|
{
|
|
INT_PTR ret = DialogBoxParamW( localised, MAKEINTRESOURCEW( id ), parent, proc, param );
|
|
if ( ( ret == -1 && GetLastError() != ERROR_SUCCESS ) && localised != original )
|
|
ret = DialogBoxParamW( original, MAKEINTRESOURCEW( id ), parent, proc, param );
|
|
return ret;
|
|
}
|
|
|
|
HWND LPCreateDialogParam( int id, HWND parent, DLGPROC proc, LPARAM param )
|
|
{
|
|
return langManager->CreateLDialogParam( language_pack_instance, hMainInstance, id, parent, proc, param );
|
|
}
|
|
|
|
HWND LPCreateDialogParamW( int id, HWND parent, DLGPROC proc, LPARAM param )
|
|
{
|
|
return langManager->CreateLDialogParamW( language_pack_instance, hMainInstance, id, parent, proc, param );
|
|
}
|
|
|
|
INT_PTR LPDialogBoxParam( int id, HWND parent, DLGPROC proc, LPARAM param )
|
|
{
|
|
return langManager->LDialogBoxParam( language_pack_instance, hMainInstance, id, parent, proc, param );
|
|
}
|
|
|
|
INT_PTR LPDialogBoxParamW( int id, HWND parent, DLGPROC proc, LPARAM param )
|
|
{
|
|
return langManager->LDialogBoxParamW( language_pack_instance, hMainInstance, id, parent, proc, param );
|
|
}
|
|
|
|
HMENU Language::LoadLMenu( HINSTANCE localised, HINSTANCE original, UINT id )
|
|
{
|
|
HMENU menu = LoadMenuA( localised, MAKEINTRESOURCEA( id ) );
|
|
if ( !menu && localised != original )
|
|
menu = LoadMenuA( original, MAKEINTRESOURCEA( id ) );
|
|
|
|
return menu;
|
|
}
|
|
|
|
HMENU Language::LoadLMenuW(HINSTANCE localised, HINSTANCE original, UINT id)
|
|
{
|
|
HMENU menu = LoadMenuW(localised, MAKEINTRESOURCEW(id));
|
|
if (!menu && localised != original)
|
|
menu = LoadMenuW(original, MAKEINTRESOURCEW(id));
|
|
return menu;
|
|
}
|
|
|
|
HACCEL Language::LoadAcceleratorsA(HINSTANCE hinst, HINSTANCE owner, LPCSTR lpTableName)
|
|
{
|
|
HACCEL hAccel = ::LoadAcceleratorsA(hinst, lpTableName);
|
|
if (!hAccel && hinst != owner)
|
|
hAccel = ::LoadAcceleratorsA(owner, lpTableName);
|
|
return hAccel;
|
|
}
|
|
|
|
HACCEL Language::LoadAcceleratorsW(HINSTANCE hinst, HINSTANCE owner, LPCWSTR lpTableName)
|
|
{
|
|
HACCEL hAccel = ::LoadAcceleratorsW(hinst, lpTableName);
|
|
if (!hAccel && hinst != owner)
|
|
hAccel = ::LoadAcceleratorsW(owner, lpTableName);
|
|
return hAccel;
|
|
}
|
|
|
|
// Implemented in 5.58+
|
|
// when we're loading a language pack we really need to specify if we're
|
|
// going to require correct use of the user's locale setting so that the
|
|
// output of certain text ie '%+6.1f' uses the correct decimal separator
|
|
// ref: http://msdn.microsoft.com/en-us/library/aa246453%28VS.60%29.aspx
|
|
BOOL Language::UseUserNumericLocale(void)
|
|
{
|
|
wchar_t tmp[4] = {0}, lang[4] = {0}, ctry[4] = {0};
|
|
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, 4);
|
|
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, ctry, 4);
|
|
|
|
// do this check to ensure that the we only change the locale
|
|
// if the language pack and the user locale identifiers match
|
|
if(!_wcsicmp(lang, GetLanguageIdentifier(LANG_LANG_CODE)) &&
|
|
!_wcsicmp(ctry, GetLanguageIdentifier(LANG_COUNTRY_CODE)) &&
|
|
GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, tmp, 4))
|
|
{
|
|
_configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
|
|
// now we set the functions to use the user's numeric locale
|
|
return !!_wsetlocale(LC_NUMERIC,tmp);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
_locale_t Language::Get_C_NumericLocale(void)
|
|
{
|
|
__declspec(thread) static _locale_t C_locale;
|
|
if(!C_locale) C_locale = _create_locale(LC_NUMERIC, "C");
|
|
return C_locale;
|
|
}
|
|
|
|
// Implemented in 5.64+
|
|
wchar_t* Language::FormattedSizeString(wchar_t *pszDest, int cchDest, __int64 size)
|
|
{
|
|
if (!pszDest) return 0;
|
|
size_t remaining = cchDest;
|
|
DWORD part = 0;
|
|
pszDest[0] = 0x00;
|
|
|
|
if (size < 1024)
|
|
{
|
|
StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS,
|
|
L"%u %s", (DWORD)(size >> 10) + ((((DWORD)(size))&1023) ? 1: 0),
|
|
getStringW(IDS_BYTES, NULL, 0));
|
|
}
|
|
else if (size < 1048576)
|
|
{
|
|
part = ((((DWORD)(size))&1023)*100) >> 10;
|
|
if (part > 0)
|
|
{
|
|
StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u.%02u %s",
|
|
(DWORD)(size >> 10), part, getStringW(geno ? IDS_KB : IDS_KIB, NULL, 0));
|
|
}
|
|
else
|
|
{
|
|
StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u %s",
|
|
(DWORD)(size >> 10), getStringW(geno ? IDS_KB : IDS_KIB, NULL, 0));
|
|
}
|
|
}
|
|
else if (size < 1073741824)
|
|
{
|
|
part = ((((DWORD)(size >> 10))&1023)*100) >> 10;
|
|
if (part > 0)
|
|
{
|
|
StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u.%02u %s",
|
|
(DWORD)(size >> 20), part, getStringW(geno ? IDS_MB : IDS_MIB, NULL, 0));
|
|
}
|
|
else
|
|
{
|
|
StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u %s",
|
|
(DWORD)(size >> 20), getStringW(geno ? IDS_MB : IDS_MIB, NULL, 0));
|
|
}
|
|
}
|
|
else if (size < 1099511627776)
|
|
{
|
|
part = ((((DWORD)(size >> 20))&1023)*100) >> 10;
|
|
if (part > 0)
|
|
{
|
|
StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u.%02u %s",
|
|
(DWORD)(size >> 30), part, getStringW(geno ? IDS_GB : IDS_GIB, NULL, 0));
|
|
}
|
|
else
|
|
{
|
|
StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u %s",
|
|
(DWORD)(size >> 30), getStringW(geno ? IDS_GB : IDS_GIB, NULL, 0));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
part = ((((DWORD)(size >> 30))&1023)*100) >> 10;
|
|
if (part > 0)
|
|
{
|
|
StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u.%02u %s",
|
|
(DWORD)(size >> 40), part, getStringW(geno ? IDS_TB : IDS_TIB, NULL, 0));
|
|
}
|
|
else
|
|
{
|
|
StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u %s",
|
|
(DWORD)(size >> 40), getStringW(geno ? IDS_TB : IDS_TIB, NULL, 0));
|
|
}
|
|
}
|
|
|
|
|
|
return pszDest;
|
|
}
|
|
|
|
HMENU LPLoadMenu(UINT id)
|
|
{
|
|
return langManager->LoadLMenu(language_pack_instance, hMainInstance, id);
|
|
}
|
|
|
|
|
|
void Lang_CleanupZip(void)
|
|
{
|
|
if (!LANGTEMPDIR[0]) return ;
|
|
if (_cleanupDirW(LANGTEMPDIR))
|
|
{
|
|
char str[78] = {0};
|
|
StringCchPrintfA(str,78,"lang_clean_up%ws",szAppName);
|
|
_w_s(str, 0);
|
|
}
|
|
}
|
|
|
|
|
|
// attempt to cleanup the last extracted temp folder for a wlz incase Winamp crashed on exit
|
|
void Lang_CleanupAfterCrash(void)
|
|
{
|
|
wchar_t buf[1024] = {0};
|
|
char str[78] = {0};
|
|
StringCchPrintfA(str,78,"lang_clean_up%ws",szAppName);
|
|
_r_sW(str, buf, sizeof(buf));
|
|
if (buf[0])
|
|
{
|
|
_cleanupDirW(buf);
|
|
_w_s(str, 0);
|
|
}
|
|
}
|
|
|
|
static int load_extra_lng(BOOL force)
|
|
{
|
|
int is_wlz = 0;
|
|
if (langManager)
|
|
{
|
|
const wchar_t *lang_identifier = langManager->GetLanguageIdentifier(LANG_IDENT_STR);
|
|
if (lang_identifier || force)
|
|
{
|
|
wchar_t extra_lang_path[MAX_PATH] = {0};
|
|
wchar_t lng_file[MAX_PATH] = {0};
|
|
|
|
if (!force)
|
|
PathCombineW(extra_lang_path, LANGDIR, lang_identifier);
|
|
else
|
|
lstrcpynW(extra_lang_path, lang_directory, MAX_PATH);
|
|
|
|
PathCombineW(lng_file, extra_lang_path, L"*.lng");
|
|
WIN32_FIND_DATAW find_data = {0};
|
|
HANDLE h = FindFirstFileW(lng_file, &find_data);
|
|
|
|
if (h != INVALID_HANDLE_VALUE)
|
|
{
|
|
do
|
|
{
|
|
PathCombineW(lng_file, extra_lang_path, find_data.cFileName);
|
|
is_wlz = 1;
|
|
winampLangStruct* templng = reinterpret_cast<winampLangStruct*>(calloc(1, sizeof(winampLangStruct)));
|
|
templng->module = _wcsdup(lng_file);
|
|
|
|
bool exception = (!lstrcmpiW(templng->module, L"omBrowser.lng") || !lstrcmpiW(templng->module, L"ml_online.lng"));
|
|
|
|
// the plain LoadLibrary(..) generally works though as we only want to
|
|
// load the lng files for resources (and that some people's lng files
|
|
// can generally end up being corrupted after a few edits), we instead
|
|
// try to load as an image / data file (so doesn't re-map things, etc)
|
|
templng->hDllInstance = LoadLibraryExW(lng_file, NULL, (!exception ? LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE : 0));
|
|
// incase of running on an older OS, try it as a plain LoadLibrary(..)
|
|
if (!templng->hDllInstance) templng->hDllInstance = LoadLibraryW(lng_file);
|
|
if (templng->hDllInstance)
|
|
{
|
|
wchar_t s[39] = {0};
|
|
if(LoadStringW(templng->hDllInstance, LANG_DLL_GUID_STRING_ID, s, 39))
|
|
{
|
|
templng->external = 1;
|
|
templng->guidstr = _wcsdup(s);
|
|
GetImageHashData(templng->hDllInstance);
|
|
}
|
|
// only keep if it's a valid lng dll ie doesn't have load issues
|
|
lnglist.push_back(templng);
|
|
}
|
|
}
|
|
while (FindNextFileW(h, &find_data));
|
|
FindClose(h);
|
|
}
|
|
}
|
|
}
|
|
return is_wlz;
|
|
}
|
|
|
|
|
|
// return 1 if we're working from a wlz otherwise return 0
|
|
int extract_wlz_to_dir(wchar_t* readme_only_wlz_extraction, BOOL *skip)
|
|
{
|
|
int is_wlz = 0;
|
|
if (config_langpack[0] || readme_only_wlz_extraction && readme_only_wlz_extraction[0])
|
|
{
|
|
wchar_t* langpack = (readme_only_wlz_extraction?readme_only_wlz_extraction:config_langpack),
|
|
tempdirbuf[MAX_PATH] = {0}, *TEMPDIR = LANGTEMPDIR;
|
|
|
|
if (_wcsicmp(extensionW(langpack), L"zip") && _wcsicmp(extensionW(langpack), L"wlz"))
|
|
{
|
|
if (PathIsFileSpecW(langpack) || PathIsRelativeW(langpack))
|
|
PathCombineW(lang_directory, LANGDIR, langpack);
|
|
else
|
|
StringCchCopyW(lang_directory, MAX_PATH, langpack);
|
|
|
|
is_wlz = load_extra_lng(TRUE);
|
|
if (skip) *skip = is_wlz;
|
|
}
|
|
else
|
|
{
|
|
wchar_t dirmask[MAX_PATH*4] = {0};
|
|
char str[78] = {0};
|
|
unzFile f = {0};
|
|
|
|
// make sure that we use a different folder from the current wlz temp folder otherwise we have issues
|
|
if(readme_only_wlz_extraction){
|
|
wchar_t buf[MAX_PATH] = {0};
|
|
GetTempPathW(MAX_PATH, buf);
|
|
GetTempFileNameW(buf, L"WLZ", GetTickCount(), tempdirbuf);
|
|
TEMPDIR = tempdirbuf;
|
|
}
|
|
|
|
CreateDirectoryW(TEMPDIR, NULL);
|
|
StringCchPrintfA(str,78,"lang_clean_up%ws",szAppName);
|
|
if(!readme_only_wlz_extraction){
|
|
StringCchCopyW(lang_directory, MAX_PATH, TEMPDIR);
|
|
_w_sW(str, TEMPDIR);
|
|
}
|
|
|
|
if (PathIsFileSpecW(langpack)|| PathIsRelativeW(langpack))
|
|
PathCombineW(dirmask, LANGDIR, langpack);
|
|
else
|
|
StringCchCopyW(dirmask, MAX_PATH*4, langpack);
|
|
|
|
// now we're going to extract, if doing a temp extraction then set the path into the passed buffer
|
|
if(readme_only_wlz_extraction){
|
|
StringCchCopyW(readme_only_wlz_extraction, MAX_PATH, TEMPDIR);
|
|
}
|
|
|
|
f = unzOpen(AutoCharFn(dirmask));
|
|
if (f)
|
|
{
|
|
if (unzGoToFirstFile(f) == UNZ_OK)
|
|
{
|
|
OVERLAPPED asyncIO = {0};
|
|
int isNT = (GetVersion() < 0x80000000);
|
|
if (isNT)
|
|
{
|
|
asyncIO.hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
|
|
asyncIO.OffsetHigh = 0;
|
|
}
|
|
do
|
|
{
|
|
char filename[MAX_PATH] = {0}, *fn = 0, *p = 0;
|
|
if (isNT)
|
|
SetEvent(asyncIO.hEvent);
|
|
unzGetCurrentFileInfo(f, NULL, filename, sizeof(filename), NULL, 0, NULL, 0);
|
|
|
|
//Only extract the file-types that could be in a skin
|
|
//If we don't filter here it's a security hole
|
|
|
|
// expand out folders if we've got a freeform based folder
|
|
if(!_strnicmp(filename,"freeform\\",9) || !_strnicmp(filename,"freeform/",9))
|
|
fn = filename;
|
|
// otherwise just extract to the root of the temp directory
|
|
else
|
|
fn = scanstr_back(filename, "\\/", filename - 1) + 1;
|
|
|
|
p = extension(fn);
|
|
// TODO: really should enum image loaders so we only extract supported image files
|
|
if (!_stricmp(p, "lng") || !_stricmp(p, "ini") || !_stricmp(p, "txt") ||
|
|
!_stricmp(p, "png") || !_stricmp(p, "bmp") || !_stricmp(p, "gif") ||
|
|
!_stricmp(p, "jpg") || !_stricmp(p, "xml") || !_stricmp(p, "htm") ||
|
|
// not too keen on dll in there but that's how the GN dlls are named
|
|
!_stricmp(p, "dll"))
|
|
{
|
|
if (unzOpenCurrentFile(f) == UNZ_OK)
|
|
{
|
|
PathCombineW(dirmask, TEMPDIR, AutoWide(fn));
|
|
CreateDirectoryForFileW(dirmask, TEMPDIR);
|
|
|
|
HANDLE fp = CreateFileW(dirmask, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | (isNT ? FILE_FLAG_OVERLAPPED : 0), NULL);
|
|
if (fp != INVALID_HANDLE_VALUE)
|
|
{
|
|
int l = 0, pos = 0, bufNum=0;
|
|
|
|
#define LANG_ZIP_BUFFER_SIZE 2048
|
|
char buf[LANG_ZIP_BUFFER_SIZE*2] = {0};
|
|
int success = 1;
|
|
do
|
|
{
|
|
DWORD written = 0;
|
|
bufNum = !bufNum;
|
|
l = unzReadCurrentFile(f, buf+LANG_ZIP_BUFFER_SIZE*bufNum, LANG_ZIP_BUFFER_SIZE);
|
|
if (!l)
|
|
unzCloseCurrentFile(f);
|
|
if (isNT)
|
|
{
|
|
WaitForSingleObject(asyncIO.hEvent, INFINITE);
|
|
if (l > 0)
|
|
{
|
|
asyncIO.Offset = pos;
|
|
if (WriteFile(fp, buf+LANG_ZIP_BUFFER_SIZE*bufNum, l, NULL, &asyncIO) == FALSE
|
|
&& GetLastError() != ERROR_IO_PENDING)
|
|
{
|
|
success=0;
|
|
}
|
|
pos += l;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (l > 0)
|
|
{
|
|
if (WriteFile(fp, buf+LANG_ZIP_BUFFER_SIZE*bufNum, l, &written, NULL) == FALSE)
|
|
success = 0;
|
|
}
|
|
}
|
|
} while (l > 0 && success);
|
|
|
|
CloseHandle(fp);
|
|
|
|
// cache information about the extracted lng files
|
|
if(!_stricmp(p, "lng") && !readme_only_wlz_extraction)
|
|
{
|
|
is_wlz = 1;
|
|
winampLangStruct* templng = reinterpret_cast<winampLangStruct*>(calloc(1, sizeof(winampLangStruct)));
|
|
templng->module = AutoWideDup(filename);
|
|
|
|
bool exception = (!lstrcmpiW(templng->module, L"omBrowser.lng") || !lstrcmpiW(templng->module, L"ml_online.lng"));
|
|
|
|
// the plain LoadLibrary(..) generally works though as we only want to
|
|
// load the lng files for resources (and that some people's lng files
|
|
// can generally end up being corrupted after a few edits), we instead
|
|
// try to load as an image / data file (so doesn't re-map things, etc)
|
|
templng->hDllInstance = LoadLibraryExW(dirmask, NULL, (!exception ? LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE : 0));
|
|
if (!templng->hDllInstance) templng->hDllInstance = LoadLibraryW(dirmask);
|
|
if (templng->hDllInstance)
|
|
{
|
|
wchar_t s[39] = {0};
|
|
if(LoadStringW(templng->hDllInstance, LANG_DLL_GUID_STRING_ID, s, 39))
|
|
{
|
|
templng->guidstr = _wcsdup(s);
|
|
GetImageHashData(templng->hDllInstance);
|
|
}
|
|
// only keep if it's a valid lng dll ie doesn't have load issues
|
|
lnglist.push_back(templng);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while (unzGoToNextFile(f) == UNZ_OK);
|
|
if (isNT && asyncIO.hEvent)
|
|
{
|
|
CloseHandle(asyncIO.hEvent);
|
|
}
|
|
}
|
|
unzClose(f);
|
|
}
|
|
}
|
|
}
|
|
else lang_directory[0] = 0;
|
|
|
|
return is_wlz;
|
|
}
|
|
|
|
|
|
HINSTANCE Language::FindDllHandleByGUID(const GUID guid)
|
|
{
|
|
wchar_t gs[40] = {0};
|
|
getGUIDstr(guid,gs);
|
|
|
|
for ( winampLangStruct *l_lng : lnglist )
|
|
{
|
|
if( l_lng->guidstr && *l_lng->guidstr && !_wcsnicmp(gs, l_lng->guidstr, 38))
|
|
return l_lng->hDllInstance;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
HINSTANCE Language::FindDllHandleByString(const char* _str)
|
|
{
|
|
AutoWide str__(_str);
|
|
const wchar_t *str = str__;
|
|
if(str && *str)
|
|
{
|
|
for ( winampLangStruct *l_lng : lnglist )
|
|
{
|
|
if ( l_lng->module && *l_lng->module && !_wcsnicmp( l_lng->module, str, lstrlenW( str ) ) )
|
|
return l_lng->hDllInstance;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
HINSTANCE Language::FindDllHandleByStringW(const wchar_t* _str)
|
|
{
|
|
AutoChar str__(_str);
|
|
const wchar_t *str = _str;
|
|
if(str && *str)
|
|
{
|
|
for ( winampLangStruct *l_lng : lnglist )
|
|
{
|
|
if( l_lng->module && *l_lng->module && !_wcsnicmp( l_lng->module, str, lstrlenW(str)))
|
|
return l_lng->hDllInstance;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
HINSTANCE Lang_InitLangSupport(HINSTANCE hinst, const GUID guid)
|
|
{
|
|
geno = _r_i("geno", 1);
|
|
started = 1;
|
|
return langManager->StartLanguageSupport(hinst, guid);
|
|
}
|
|
|
|
|
|
void Lang_FollowUserDecimalLocale(void)
|
|
{
|
|
langManager->UseUserNumericLocale();
|
|
}
|
|
|
|
|
|
// use this to load based on the module specified so that we make sure
|
|
// we've got the correct hinstance based on lng file or default handle
|
|
HINSTANCE Language::StartLanguageSupport(HINSTANCE hinstance, const GUID guid)
|
|
{
|
|
if (!g_safeMode)
|
|
{
|
|
HWND agent = FindWindowW(L"WinampAgentMain", NULL);
|
|
wchar_t winampaLngPath[MAX_PATH] = {0};
|
|
int is_wlz = 0;
|
|
|
|
// if we find Winamp Agent running then we need to tell it
|
|
// to unload it's winampa.lng for what we're about to do..
|
|
if (IsWindow(agent) && !already_extracted)
|
|
{
|
|
SendMessageW(agent, WM_USER + 16, 1, 0);
|
|
}
|
|
|
|
// always remove winampa.lng just incase we crashed and it leaves things out of synch
|
|
if(!already_extracted){
|
|
StringCchPrintfW(winampaLngPath, MAX_PATH, L"%s\\winampa.lng", CONFIGDIR);
|
|
DeleteFileW(winampaLngPath);
|
|
}
|
|
|
|
config_load_langpack_var();
|
|
if(!already_extracted)
|
|
{
|
|
BOOL skip = FALSE;
|
|
already_extracted = 1;
|
|
prev_wlz_ex_state = is_wlz = extract_wlz_to_dir(0, &skip);
|
|
if (!skip) load_extra_lng(FALSE);
|
|
else LANGTEMPDIR[0] = 0;
|
|
}
|
|
else
|
|
{
|
|
is_wlz = prev_wlz_ex_state;
|
|
agent = 0;
|
|
}
|
|
|
|
// make sure that we don't try and load the exe/dll being localised as the lng dll
|
|
wchar_t modulename[MAX_PATH] = {0}, *p = 0;
|
|
GetModuleFileNameW(hinstance, modulename, MAX_PATH);
|
|
p = scanstr_backW(modulename, L"\\/", NULL);
|
|
if(p) p = CharNextW(p);
|
|
|
|
// if is_wlz != 0 then we can attempt to use the wlz extracted files otherwise
|
|
// (for the time being) we drop back to the older lng pack system
|
|
// either way we still need to make sure that what we're using is valid
|
|
if (config_langpack[0] && is_wlz)
|
|
{
|
|
HMODULE h = langManager->FindDllHandleByGUID(guid);
|
|
if(!h) // possible fallback usage if things failed to work on guid look up
|
|
{ // though wouldn't be reliable if people change the lng file names
|
|
wchar_t tmpfile[MAX_PATH], *t = 0;
|
|
lstrcpynW(tmpfile,p,MAX_PATH);
|
|
t = scanstr_backW(tmpfile, L".", NULL);
|
|
lstrcpynW(t,L".lng",MAX_PATH);
|
|
h = langManager->FindDllHandleByStringW(tmpfile);
|
|
}
|
|
|
|
if (h)
|
|
{
|
|
// if the wlz was able to be loaded (as we believe at this point)
|
|
// then we see if Winamp Agent is running and tell it to refresh
|
|
// it's version of winampa.lng once we've copied into %inidir%
|
|
if (IsWindow(agent))
|
|
{
|
|
// copy from the wlz folder to the settings folder
|
|
wchar_t winampaWlzPath[MAX_PATH] = {0};
|
|
StringCchPrintfW(winampaWlzPath, MAX_PATH, L"%s\\winampa.lng", lang_directory);
|
|
CopyFileW(winampaWlzPath,winampaLngPath,FALSE);
|
|
SendMessageW(agent, WM_USER + 16, 0, 0);
|
|
}
|
|
|
|
// if we get here then we've managed to load the language pack
|
|
// (still could be invalid but that's generally from failed dll files)
|
|
return h;
|
|
}
|
|
}
|
|
}
|
|
|
|
// make sure we return the passed hinstance incase of failure to load/invalid lng file/etc
|
|
return hinstance;
|
|
}
|
|
|
|
|
|
void Lang_EndLangSupport(void)
|
|
{
|
|
started = 0;
|
|
|
|
// need to fully clean up things here including unloading of the langpack
|
|
HINSTANCE old_language_pack_instance = language_pack_instance;
|
|
if(language_pack_instance != hMainInstance)
|
|
{
|
|
FreeLibrary(language_pack_instance);
|
|
language_pack_instance = hMainInstance;
|
|
}
|
|
|
|
for ( winampLangStruct *l_lng : lnglist )
|
|
{
|
|
if( l_lng->module)
|
|
{
|
|
free( l_lng->module);
|
|
l_lng->module = 0;
|
|
}
|
|
|
|
if( l_lng->guidstr)
|
|
{
|
|
free( l_lng->guidstr);
|
|
l_lng->guidstr = 0;
|
|
}
|
|
|
|
// this check is to prevent trying to re-free the winamp.lng (as it's done earlier)
|
|
// as well as anything which is not in the temp folder to avoid any unloading issues
|
|
if ( !l_lng->external && l_lng->hDllInstance && ( l_lng->hDllInstance != old_language_pack_instance ) )
|
|
{
|
|
FreeLibrary( l_lng->hDllInstance );
|
|
l_lng->hDllInstance = 0;
|
|
}
|
|
}
|
|
|
|
lnglist.clear();
|
|
prev_wlz_ex_state = already_extracted = 0;
|
|
}
|
|
|
|
|
|
HINSTANCE Lang_FakeWinampLangHInst(HINSTANCE adjustedHInst){
|
|
HINSTANCE previousHInst = language_pack_instance;
|
|
language_pack_instance = adjustedHInst;
|
|
started = !!adjustedHInst;
|
|
return previousHInst;
|
|
}
|
|
|
|
|
|
void Lang_LocaliseAgentOnTheFly(BOOL refresh){
|
|
// if we need to refresh then attempt to use the winampa.lng from the
|
|
// current language pack if one is present and has been extracted so
|
|
// we test to see if we've extracted a language pack already
|
|
if(already_extracted){
|
|
HWND agent = FindWindowW(L"WinampAgentMain", NULL);
|
|
wchar_t winampaLngPath[MAX_PATH] = {0};
|
|
|
|
// if we find Winamp Agent running then we need to tell it
|
|
// to unload it's winampa.lng for what we're about to do...
|
|
// although this is likely to be a new load, doing this will
|
|
// help to ensure that things are unloaded incase of issues
|
|
if(IsWindow(agent)){
|
|
SendMessageW(agent, WM_USER + 16, 1, 0);
|
|
}
|
|
|
|
// always remove winampa.lng just incase we crashed and it leaves things out of synch
|
|
StringCchPrintfW(winampaLngPath, MAX_PATH, L"%s\\winampa.lng", CONFIGDIR);
|
|
DeleteFileW(winampaLngPath);
|
|
|
|
if(refresh){
|
|
wchar_t winampaWlzPath[MAX_PATH] = {0};
|
|
StringCchPrintfW(winampaWlzPath, MAX_PATH, L"%s\\winampa.lng", lang_directory);
|
|
CopyFileW(winampaWlzPath,winampaLngPath,FALSE);
|
|
SendMessageW(agent, WM_USER + 16, 0, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CBCLASS
|
|
#undef CBCLASS
|
|
#endif
|
|
|
|
#define CBCLASS Language
|
|
START_DISPATCH;
|
|
CB( API_LANGUAGE_GETSTRING, GetString )
|
|
CB( API_LANGUAGE_GETSTRINGW, GetStringW )
|
|
CB( API_LANGUAGE_GETSTRINGFROMGUID, GetStringFromGUID )
|
|
CB( API_LANGUAGE_GETSTRINGFROMGUIDW, GetStringFromGUIDW )
|
|
CB( API_LANGUAGE_GETHINSTANCEBYGUID, FindDllHandleByGUID )
|
|
CB( API_LANGUAGE_GETHINSTANCEBYNAME, FindDllHandleByString )
|
|
CB( API_LANGUAGE_GETHINSTANCEBYNAMEW, FindDllHandleByStringW )
|
|
CB( API_LANGUAGE_STARTUP, StartLanguageSupport )
|
|
CB( API_LANGUAGE_GETLANGUAGEFOLDER, GetLanguageFolder )
|
|
CB( API_LANGUAGE_CREATELDIALOGPARAM, CreateLDialogParam )
|
|
CB( API_LANGUAGE_LDIALOGBOXPARAM, LDialogBoxParam )
|
|
CB( API_LANGUAGE_LOADLMENU, LoadLMenu )
|
|
CB( API_LANGUAGE_CREATELDIALOGPARAMW, CreateLDialogParamW )
|
|
CB( API_LANGUAGE_LDIALOGBOXPARAMW, LDialogBoxParamW )
|
|
CB( API_LANGUAGE_LOADLMENUW, LoadLMenuW )
|
|
CB( API_LANGUAGE_GETLANGUAGEIDENTIFIER, GetLanguageIdentifier )
|
|
CB( API_LANGUAGE_LOADRESOURCEFROMFILEA, LoadResourceFromFile )
|
|
CB( API_LANGUAGE_LOADRESOURCEFROMFILEW, LoadResourceFromFileW )
|
|
CB( API_LANGUAGE_LOADACCELERATORSA, LoadAcceleratorsA )
|
|
CB( API_LANGUAGE_LOADACCELERATORSW, LoadAcceleratorsW )
|
|
CB( API_LANGUAGE_USEUSERNUMERICLOCALE, UseUserNumericLocale )
|
|
CB( API_LANGUAGE_GET_C_NUMERICLOCALE, Get_C_NumericLocale )
|
|
CB( API_LANGUAGE_FORMATTEDSIZESTRING, FormattedSizeString )
|
|
END_DISPATCH |