winamp/Src/Winamp/lang.cpp
2024-09-24 14:54:57 +02:00

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