aboutsummaryrefslogtreecommitdiff
path: root/Src/Winamp/lang.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Winamp/lang.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Winamp/lang.cpp')
-rw-r--r--Src/Winamp/lang.cpp1136
1 files changed, 1136 insertions, 0 deletions
diff --git a/Src/Winamp/lang.cpp b/Src/Winamp/lang.cpp
new file mode 100644
index 00000000..5c2f9581
--- /dev/null
+++ b/Src/Winamp/lang.cpp
@@ -0,0 +1,1136 @@
+/** (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 \ No newline at end of file