1
0

lang.cpp 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136
  1. /** (c) Nullsoft, Inc. C O N F I D E N T I A L
  2. ** Filename:
  3. ** Project:
  4. ** Description: Utility functions for handling language support
  5. ** Author:
  6. ** Created:
  7. **/
  8. #include <locale.h>
  9. #include "main.h"
  10. #include "language.h"
  11. #include "../nu/AutoWide.h"
  12. #include "../nu/AutoChar.h"
  13. #include "minizip/unzip.h"
  14. #include <vector>
  15. #include "../nu/AutoCharFn.h"
  16. typedef struct {
  17. wchar_t *module;
  18. wchar_t *guidstr; // generally is 0 or 39 (38+null)
  19. int guid;
  20. int external;
  21. HINSTANCE hDllInstance;
  22. } winampLangStruct;
  23. static std::vector<winampLangStruct*> lnglist;
  24. int already_extracted = 0, prev_wlz_ex_state = 0, geno = 1, started = 0;
  25. // data storage of the read in values requires just the hash and the id to be stored
  26. // and has to be logged against the different id types so just need a list of structs
  27. // based against each type (dialog, string resource, etc)
  28. // for the moment we deal with the following resource types
  29. // RT_DIALOG, RT_MENU, RT_STRING & custom resources
  30. // so for initial implementation we only need to store upto 4 hash+id lists
  31. #if 0
  32. typedef struct {
  33. int id;
  34. char id_str[32];
  35. char hash[17];
  36. char str[64];
  37. } hashstruct;
  38. std::vector<hashstruct*> dialogList;
  39. std::vector<hashstruct*> menuList;
  40. std::vector<hashstruct*> stringList; // will be the largest of the lot
  41. std::vector<hashstruct*> customList; // should be very few of this
  42. #endif
  43. // have section header (text or int id)
  44. // then the hash and then the id that's related the hash eg the dialog resource id
  45. void ReadHashFileDetails(char* data, DWORD datalen)
  46. {
  47. #if 0
  48. char* p = data, *s = p, *t = 0, *u;
  49. while(s && *s)
  50. {
  51. // is it the start of a block that we've just gotten to...
  52. if(*s == '@' || *s == '#')
  53. {
  54. int id = -1;
  55. char id_str[32] = {0};
  56. u = s = CharNext(s);
  57. if(!*s){break;}
  58. // advance to the end of the line to get the block identifier
  59. // would need to use the @ or # to process the type used
  60. // ie if a type 5 then only use on dialog loading calls
  61. while(u && *u && *u != '\n'){u = CharNext(u);}
  62. if(*u == '\n'){u = CharNext(u);*CharPrev(p,u) = 0;}
  63. if(!*u){break;}
  64. // identifier of the block is found here :)
  65. if(*s)
  66. {
  67. id = atoi(s);
  68. if(!id)
  69. {
  70. lstrcpyn(id_str, s, sizeof(id_str));
  71. }
  72. }
  73. *CharPrev(p,u) = '\n';
  74. while(s && *s && (*s != '@' && *s != '#'))
  75. {
  76. int end = 0;
  77. while(s && *s && *s != '\n'){s = CharNext(s);}
  78. if(*s == '\n'){s = CharNext(s);}
  79. // if nothing else then need to abort (since we don't want to do bad things)
  80. // and have to take into account where in the buffer we are otherwise we can
  81. // end up going into the next part of the dll/exe resource data due to how
  82. // it is all stored/handled in them (ie butted up against each other)
  83. if(!*s || s >= p+datalen){break;}
  84. t = s;
  85. // do a check after we've advanced to the start of a new line
  86. // so that we can see if we've hit a new resource type block
  87. if(*s == '@' || *s == '#')
  88. {
  89. s = CharPrev(p,s);
  90. break;
  91. }
  92. // scan through to the start of the second part of the <hash:id> block
  93. while(t && *t && *t != ':')
  94. {
  95. t = CharNext(t);
  96. }
  97. if(*t == ':')
  98. {
  99. t = CharNext(t);
  100. *CharPrev(p,t) = 0;
  101. }
  102. // scan through to the end of the line so that we then have the id
  103. u = t;
  104. while(u && *u && *u != '\n')
  105. {
  106. u = CharNext(u);
  107. }
  108. if(*u == '\n')
  109. {
  110. u = CharNext(u);
  111. *CharPrev(p,u) = 0;
  112. }
  113. // hash and identifier of the entry is found here :)
  114. // -> need to check how it works with IDD_CRASHDLG$()
  115. if(*s)
  116. {
  117. hashstruct* tempList = reinterpret_cast<hashstruct*>(calloc(1, sizeof(hashstruct)));
  118. ZeroMemory(tempList,sizeof(hashstruct));
  119. /*if(*t == 1) wsprintf(a,"%s %d (%s)\n", s, *t, t+1);
  120. else wsprintf(a,"%s %s (%d)\n", s, t+1, *t);*/
  121. if(*t == 1) // int_id
  122. lstrcpyn(tempList->str, t+1, sizeof(tempList->str));
  123. else // string_id
  124. lstrcpyn(tempList->str, t+1, *t/*sizeof(tempList->str)*/);
  125. lstrcpyn(tempList->hash, s, sizeof(tempList->hash));
  126. if(id) tempList->id = id;
  127. switch(id)
  128. {
  129. case RT_MENU:
  130. {
  131. menuList.push_back(tempList);
  132. }
  133. break;
  134. case RT_DIALOG:
  135. {
  136. dialogList.push_back(tempList);
  137. }
  138. break;
  139. case RT_STRING:
  140. {
  141. stringList.push_back(tempList);
  142. }
  143. break;
  144. default:
  145. // only do if there's no id from atoi (indicates a custom resource id)
  146. if(!id)
  147. {
  148. lstrcpyn(tempList->id_str, id_str, sizeof(tempList->id_str));
  149. customList.push_back(tempList);
  150. }
  151. break;
  152. }
  153. {
  154. char zz[100] = {0};
  155. StringCchPrintf(zz,100,"ID: '%s' %d\t%s %s\n",
  156. tempList->id_str, tempList->id,
  157. tempList->hash, tempList->str);
  158. OutputDebugString(zz);
  159. }
  160. }
  161. *CharPrev(p,u) = '\n';
  162. s = CharPrev(p,u);
  163. }
  164. }
  165. s = CharNext(s);
  166. }
  167. #endif
  168. }
  169. int GetImageHashData(HINSTANCE imageInstance)
  170. {
  171. DWORD datalen = 0;
  172. void* data = langManager->LoadResourceFromFile(imageInstance,imageInstance,L"HASH",L"HASH",&datalen);
  173. ReadHashFileDetails((char*)data,datalen);
  174. UnlockResource(data);
  175. FreeResource(data);
  176. return 0;
  177. }
  178. #ifdef _DEBUG
  179. static void CheckLangThread()
  180. {
  181. if (mainThreadId != GetCurrentThreadId())
  182. {
  183. // DebugBreak();
  184. /**
  185. ** If you hit this breakpoint, it's because you tried to use WASABI_API_LANG->GetString on another thread,
  186. ** without supplying your own buffer.
  187. ** You really don't want to be doing that.
  188. ** Hit alt+7, check what function is calling GetString/GetStringW and go fix it.
  189. ** Now.
  190. **/
  191. }
  192. }
  193. #else
  194. #define CheckLangThread()
  195. #endif
  196. char *getString(UINT uID, char *str, size_t maxlen)
  197. {
  198. return langManager->GetString(language_pack_instance,hMainInstance, uID, str, maxlen);
  199. }
  200. int LPMessageBox(HWND parent, UINT idMessage, UINT idTitle, UINT type)
  201. {
  202. wchar_t message[32768] = {0};
  203. wchar_t title[256] = {0};
  204. // 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
  205. // return MessageBoxExW(parent,getStringW(idMessage,message,32768),getStringW(idTitle,title,256),type,MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH));
  206. return MessageBoxW(parent,getStringW(idMessage,message,32768),getStringW(idTitle,title,256),type);
  207. }
  208. char* Language::GetString(HINSTANCE hinst, HINSTANCE owner, UINT uID, char *str, size_t maxlen)
  209. {
  210. __declspec(thread) static char *buf;
  211. if (!str)
  212. {
  213. CheckLangThread();
  214. if (!buf)
  215. buf = (char *)calloc(LANG_STATIC_BUFFER_SIZE, sizeof(buf[0]));
  216. str = buf;
  217. maxlen = LANG_STATIC_BUFFER_SIZE;
  218. }
  219. // sometimes we need to ignore things i.e. accessing on closing, etc
  220. if (((unsigned long)str >= 65536) && !LoadStringA((started ? hinst : owner), uID, str, (int)maxlen))
  221. {
  222. if (hinst == owner || !LoadStringA(owner, uID, str, (int)maxlen))
  223. {
  224. lstrcpynA(str, "Error loading string", (int)maxlen);
  225. }
  226. }
  227. return str;
  228. }
  229. wchar_t *getStringW(UINT uID, wchar_t *str, size_t maxlen)
  230. {
  231. return langManager->GetStringW(language_pack_instance,hMainInstance, uID, str, maxlen);
  232. }
  233. wchar_t* Language::GetStringW(HINSTANCE hinst, HINSTANCE owner, UINT uID, wchar_t *str, size_t maxlen)
  234. {
  235. __declspec(thread) static wchar_t *buf;
  236. if (!str)
  237. {
  238. CheckLangThread();
  239. if (!buf)
  240. buf = (wchar_t *)calloc(LANG_STATIC_BUFFER_SIZE, sizeof(buf[0]));
  241. str = buf;
  242. maxlen = LANG_STATIC_BUFFER_SIZE;
  243. }
  244. // sometimes we need to ignore things i.e. accessing on closing, etc
  245. if (((unsigned long)str >= 65536) && !LoadStringW((started ? hinst : owner), uID, str, (int)maxlen))
  246. {
  247. if (hinst == owner || !LoadStringW(owner, uID, str, (int)maxlen))
  248. {
  249. lstrcpynW(str, L"Error loading string", (int)maxlen);
  250. }
  251. }
  252. return str;
  253. }
  254. char* Language::GetStringFromGUID(const GUID guid, HINSTANCE owner, UINT uID, char *str, size_t maxlen)
  255. {
  256. __declspec(thread) static char *buf;
  257. if (!str)
  258. {
  259. CheckLangThread();
  260. if (!buf)
  261. buf = (char *)calloc(LANG_STATIC_BUFFER_SIZE, sizeof(buf[0]));
  262. str = buf;
  263. maxlen = LANG_STATIC_BUFFER_SIZE;
  264. }
  265. HINSTANCE tl = FindDllHandleByGUID(guid);
  266. if(!tl) tl = owner;
  267. // sometimes we need to ignore things i.e. accessing on closing, etc
  268. if (((unsigned long)str >= 65536) && !LoadStringA((started ? tl : owner), uID, str, (int)maxlen))
  269. {
  270. if (!LoadStringA(owner, uID, str, (int)maxlen))
  271. {
  272. lstrcpynA(str, "Error loading string", (int)maxlen);
  273. }
  274. }
  275. return str;
  276. }
  277. wchar_t* Language::GetStringFromGUIDW(const GUID guid, HINSTANCE owner, UINT uID, wchar_t *str, size_t maxlen)
  278. {
  279. __declspec(thread) static wchar_t *buf;
  280. if (!str)
  281. {
  282. CheckLangThread();
  283. if (!buf)
  284. buf = (wchar_t *)calloc(LANG_STATIC_BUFFER_SIZE, sizeof(buf[0]));
  285. str = buf;
  286. maxlen = LANG_STATIC_BUFFER_SIZE;
  287. }
  288. HINSTANCE tl = FindDllHandleByGUID(guid);
  289. if(!tl) tl = owner;
  290. // sometimes we need to ignore things i.e. accessing on closing, etc
  291. if (((unsigned long)str >= 65536) && !LoadStringW((started ? tl : owner), uID, str, (int) maxlen))
  292. {
  293. if (!LoadStringW(owner, uID, str, (int) maxlen))
  294. {
  295. lstrcpynW(str, L"Error loading string", (int) maxlen);
  296. }
  297. }
  298. return str;
  299. }
  300. const wchar_t *Language::GetLanguageFolder()
  301. {
  302. return (LANGTEMPDIR[0] ? LANGTEMPDIR : lang_directory);
  303. }
  304. void* Language::LoadResourceFromFileW(HINSTANCE hinst, HINSTANCE owner, LPCWSTR lpType, LPCWSTR lpName, DWORD* size)
  305. {
  306. HINSTANCE hmod = hinst;
  307. HRSRC rsrc = FindResourceW(hmod, lpName, lpType);
  308. if(!rsrc)
  309. {
  310. hmod = owner;
  311. rsrc = FindResourceW(hmod, lpName, lpType);
  312. }
  313. if(rsrc)
  314. {
  315. HGLOBAL resourceHandle = LoadResource(hmod, rsrc);
  316. if(size){*size = SizeofResource(hmod, rsrc);}
  317. return LockResource(resourceHandle);
  318. }
  319. return 0;
  320. }
  321. void* Language::LoadResourceFromFile(HINSTANCE hinst, HINSTANCE owner, LPCTSTR lpType, LPCTSTR lpName, DWORD* size)
  322. {
  323. HINSTANCE hmod = hinst;
  324. HRSRC rsrc = FindResource(hmod, lpName, lpType);
  325. if(!rsrc)
  326. {
  327. hmod = owner;
  328. rsrc = FindResource(hmod, lpName, lpType);
  329. }
  330. if(rsrc)
  331. {
  332. HGLOBAL resourceHandle = LoadResource(hmod, rsrc);
  333. if(size){*size = SizeofResource(hmod, rsrc);}
  334. return LockResource(resourceHandle);
  335. }
  336. return 0;
  337. }
  338. const wchar_t *Language::GetLanguageIdentifier( int mode )
  339. {
  340. static wchar_t id_str[ 9 ] = { 0 };
  341. id_str[ 0 ] = 0;
  342. // 5.58 fix - was returning en-US on all calls to this via load_extra_lng(..)
  343. // make sure to try to use a loaded winamp.lng as load_extra_lng(..) relies on
  344. // this for the path to use but calls it before getStringW(..) will work fully
  345. GetStringFromGUIDW( WinampLangGUID, hMainInstance, LANG_PACK_LANG_ID, id_str, 9 );
  346. if ( !_wcsicmp( id_str, L"Error l" ) )
  347. {
  348. id_str[ 0 ] = 0;
  349. }
  350. if ( mode && id_str[ 0 ] )
  351. {
  352. wchar_t *iStr = id_str;
  353. while ( iStr && *iStr && *iStr != L'-' )
  354. {
  355. iStr = CharNextW( iStr );
  356. }
  357. if ( iStr && *iStr == '-' )
  358. {
  359. iStr = CharNextW( iStr );
  360. *CharPrevW( id_str, iStr ) = 0;
  361. }
  362. if ( mode == LANG_LANG_CODE )
  363. return id_str;
  364. else if ( mode == LANG_COUNTRY_CODE )
  365. return iStr;
  366. }
  367. return ( id_str[ 0 ] ? id_str : 0 );
  368. }
  369. HWND Language::CreateLDialogParam( HINSTANCE localised, HINSTANCE original, UINT id, HWND parent, DLGPROC proc, LPARAM param )
  370. {
  371. HWND hwnd = (HWND)CreateDialogParamA( localised, MAKEINTRESOURCEA( id ), parent, proc, param );
  372. if ( !hwnd && localised != original )
  373. hwnd = (HWND)CreateDialogParamA( original, MAKEINTRESOURCEA( id ), parent, proc, param );
  374. return hwnd;
  375. }
  376. HWND Language::CreateLDialogParamW( HINSTANCE localised, HINSTANCE original, UINT id, HWND parent, DLGPROC proc, LPARAM param )
  377. {
  378. HWND hwnd = (HWND)CreateDialogParamW( localised, MAKEINTRESOURCEW( id ), parent, proc, param );
  379. if ( !hwnd && localised != original )
  380. hwnd = (HWND)CreateDialogParamW( original, MAKEINTRESOURCEW( id ), parent, proc, param );
  381. return hwnd;
  382. }
  383. INT_PTR Language::LDialogBoxParam( HINSTANCE localised, HINSTANCE original, UINT id, HWND parent, DLGPROC proc, LPARAM param )
  384. {
  385. INT_PTR ret = DialogBoxParamA( localised, MAKEINTRESOURCEA( id ), parent, proc, param );
  386. if ( ( ret == -1 && GetLastError() != ERROR_SUCCESS ) && localised != original )
  387. ret = DialogBoxParamA( original, MAKEINTRESOURCEA( id ), parent, proc, param );
  388. return ret;
  389. }
  390. INT_PTR Language::LDialogBoxParamW( HINSTANCE localised, HINSTANCE original, UINT id, HWND parent, DLGPROC proc, LPARAM param )
  391. {
  392. INT_PTR ret = DialogBoxParamW( localised, MAKEINTRESOURCEW( id ), parent, proc, param );
  393. if ( ( ret == -1 && GetLastError() != ERROR_SUCCESS ) && localised != original )
  394. ret = DialogBoxParamW( original, MAKEINTRESOURCEW( id ), parent, proc, param );
  395. return ret;
  396. }
  397. HWND LPCreateDialogParam( int id, HWND parent, DLGPROC proc, LPARAM param )
  398. {
  399. return langManager->CreateLDialogParam( language_pack_instance, hMainInstance, id, parent, proc, param );
  400. }
  401. HWND LPCreateDialogParamW( int id, HWND parent, DLGPROC proc, LPARAM param )
  402. {
  403. return langManager->CreateLDialogParamW( language_pack_instance, hMainInstance, id, parent, proc, param );
  404. }
  405. INT_PTR LPDialogBoxParam( int id, HWND parent, DLGPROC proc, LPARAM param )
  406. {
  407. return langManager->LDialogBoxParam( language_pack_instance, hMainInstance, id, parent, proc, param );
  408. }
  409. INT_PTR LPDialogBoxParamW( int id, HWND parent, DLGPROC proc, LPARAM param )
  410. {
  411. return langManager->LDialogBoxParamW( language_pack_instance, hMainInstance, id, parent, proc, param );
  412. }
  413. HMENU Language::LoadLMenu( HINSTANCE localised, HINSTANCE original, UINT id )
  414. {
  415. HMENU menu = LoadMenuA( localised, MAKEINTRESOURCEA( id ) );
  416. if ( !menu && localised != original )
  417. menu = LoadMenuA( original, MAKEINTRESOURCEA( id ) );
  418. return menu;
  419. }
  420. HMENU Language::LoadLMenuW(HINSTANCE localised, HINSTANCE original, UINT id)
  421. {
  422. HMENU menu = LoadMenuW(localised, MAKEINTRESOURCEW(id));
  423. if (!menu && localised != original)
  424. menu = LoadMenuW(original, MAKEINTRESOURCEW(id));
  425. return menu;
  426. }
  427. HACCEL Language::LoadAcceleratorsA(HINSTANCE hinst, HINSTANCE owner, LPCSTR lpTableName)
  428. {
  429. HACCEL hAccel = ::LoadAcceleratorsA(hinst, lpTableName);
  430. if (!hAccel && hinst != owner)
  431. hAccel = ::LoadAcceleratorsA(owner, lpTableName);
  432. return hAccel;
  433. }
  434. HACCEL Language::LoadAcceleratorsW(HINSTANCE hinst, HINSTANCE owner, LPCWSTR lpTableName)
  435. {
  436. HACCEL hAccel = ::LoadAcceleratorsW(hinst, lpTableName);
  437. if (!hAccel && hinst != owner)
  438. hAccel = ::LoadAcceleratorsW(owner, lpTableName);
  439. return hAccel;
  440. }
  441. // Implemented in 5.58+
  442. // when we're loading a language pack we really need to specify if we're
  443. // going to require correct use of the user's locale setting so that the
  444. // output of certain text ie '%+6.1f' uses the correct decimal separator
  445. // ref: http://msdn.microsoft.com/en-us/library/aa246453%28VS.60%29.aspx
  446. BOOL Language::UseUserNumericLocale(void)
  447. {
  448. wchar_t tmp[4] = {0}, lang[4] = {0}, ctry[4] = {0};
  449. GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SISO639LANGNAME, lang, 4);
  450. GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SISO3166CTRYNAME, ctry, 4);
  451. // do this check to ensure that the we only change the locale
  452. // if the language pack and the user locale identifiers match
  453. if(!_wcsicmp(lang, GetLanguageIdentifier(LANG_LANG_CODE)) &&
  454. !_wcsicmp(ctry, GetLanguageIdentifier(LANG_COUNTRY_CODE)) &&
  455. GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, tmp, 4))
  456. {
  457. _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
  458. // now we set the functions to use the user's numeric locale
  459. return !!_wsetlocale(LC_NUMERIC,tmp);
  460. }
  461. return FALSE;
  462. }
  463. _locale_t Language::Get_C_NumericLocale(void)
  464. {
  465. __declspec(thread) static _locale_t C_locale;
  466. if(!C_locale) C_locale = _create_locale(LC_NUMERIC, "C");
  467. return C_locale;
  468. }
  469. // Implemented in 5.64+
  470. wchar_t* Language::FormattedSizeString(wchar_t *pszDest, int cchDest, __int64 size)
  471. {
  472. if (!pszDest) return 0;
  473. size_t remaining = cchDest;
  474. DWORD part = 0;
  475. pszDest[0] = 0x00;
  476. if (size < 1024)
  477. {
  478. StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS,
  479. L"%u %s", (DWORD)(size >> 10) + ((((DWORD)(size))&1023) ? 1: 0),
  480. getStringW(IDS_BYTES, NULL, 0));
  481. }
  482. else if (size < 1048576)
  483. {
  484. part = ((((DWORD)(size))&1023)*100) >> 10;
  485. if (part > 0)
  486. {
  487. StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u.%02u %s",
  488. (DWORD)(size >> 10), part, getStringW(geno ? IDS_KB : IDS_KIB, NULL, 0));
  489. }
  490. else
  491. {
  492. StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u %s",
  493. (DWORD)(size >> 10), getStringW(geno ? IDS_KB : IDS_KIB, NULL, 0));
  494. }
  495. }
  496. else if (size < 1073741824)
  497. {
  498. part = ((((DWORD)(size >> 10))&1023)*100) >> 10;
  499. if (part > 0)
  500. {
  501. StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u.%02u %s",
  502. (DWORD)(size >> 20), part, getStringW(geno ? IDS_MB : IDS_MIB, NULL, 0));
  503. }
  504. else
  505. {
  506. StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u %s",
  507. (DWORD)(size >> 20), getStringW(geno ? IDS_MB : IDS_MIB, NULL, 0));
  508. }
  509. }
  510. else if (size < 1099511627776)
  511. {
  512. part = ((((DWORD)(size >> 20))&1023)*100) >> 10;
  513. if (part > 0)
  514. {
  515. StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u.%02u %s",
  516. (DWORD)(size >> 30), part, getStringW(geno ? IDS_GB : IDS_GIB, NULL, 0));
  517. }
  518. else
  519. {
  520. StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u %s",
  521. (DWORD)(size >> 30), getStringW(geno ? IDS_GB : IDS_GIB, NULL, 0));
  522. }
  523. }
  524. else
  525. {
  526. part = ((((DWORD)(size >> 30))&1023)*100) >> 10;
  527. if (part > 0)
  528. {
  529. StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u.%02u %s",
  530. (DWORD)(size >> 40), part, getStringW(geno ? IDS_TB : IDS_TIB, NULL, 0));
  531. }
  532. else
  533. {
  534. StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%u %s",
  535. (DWORD)(size >> 40), getStringW(geno ? IDS_TB : IDS_TIB, NULL, 0));
  536. }
  537. }
  538. return pszDest;
  539. }
  540. HMENU LPLoadMenu(UINT id)
  541. {
  542. return langManager->LoadLMenu(language_pack_instance, hMainInstance, id);
  543. }
  544. void Lang_CleanupZip(void)
  545. {
  546. if (!LANGTEMPDIR[0]) return ;
  547. if (_cleanupDirW(LANGTEMPDIR))
  548. {
  549. char str[78] = {0};
  550. StringCchPrintfA(str,78,"lang_clean_up%ws",szAppName);
  551. _w_s(str, 0);
  552. }
  553. }
  554. // attempt to cleanup the last extracted temp folder for a wlz incase Winamp crashed on exit
  555. void Lang_CleanupAfterCrash(void)
  556. {
  557. wchar_t buf[1024] = {0};
  558. char str[78] = {0};
  559. StringCchPrintfA(str,78,"lang_clean_up%ws",szAppName);
  560. _r_sW(str, buf, sizeof(buf));
  561. if (buf[0])
  562. {
  563. _cleanupDirW(buf);
  564. _w_s(str, 0);
  565. }
  566. }
  567. static int load_extra_lng(BOOL force)
  568. {
  569. int is_wlz = 0;
  570. if (langManager)
  571. {
  572. const wchar_t *lang_identifier = langManager->GetLanguageIdentifier(LANG_IDENT_STR);
  573. if (lang_identifier || force)
  574. {
  575. wchar_t extra_lang_path[MAX_PATH] = {0};
  576. wchar_t lng_file[MAX_PATH] = {0};
  577. if (!force)
  578. PathCombineW(extra_lang_path, LANGDIR, lang_identifier);
  579. else
  580. lstrcpynW(extra_lang_path, lang_directory, MAX_PATH);
  581. PathCombineW(lng_file, extra_lang_path, L"*.lng");
  582. WIN32_FIND_DATAW find_data = {0};
  583. HANDLE h = FindFirstFileW(lng_file, &find_data);
  584. if (h != INVALID_HANDLE_VALUE)
  585. {
  586. do
  587. {
  588. PathCombineW(lng_file, extra_lang_path, find_data.cFileName);
  589. is_wlz = 1;
  590. winampLangStruct* templng = reinterpret_cast<winampLangStruct*>(calloc(1, sizeof(winampLangStruct)));
  591. templng->module = _wcsdup(lng_file);
  592. bool exception = (!lstrcmpiW(templng->module, L"omBrowser.lng") || !lstrcmpiW(templng->module, L"ml_online.lng"));
  593. // the plain LoadLibrary(..) generally works though as we only want to
  594. // load the lng files for resources (and that some people's lng files
  595. // can generally end up being corrupted after a few edits), we instead
  596. // try to load as an image / data file (so doesn't re-map things, etc)
  597. templng->hDllInstance = LoadLibraryExW(lng_file, NULL, (!exception ? LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE : 0));
  598. // incase of running on an older OS, try it as a plain LoadLibrary(..)
  599. if (!templng->hDllInstance) templng->hDllInstance = LoadLibraryW(lng_file);
  600. if (templng->hDllInstance)
  601. {
  602. wchar_t s[39] = {0};
  603. if(LoadStringW(templng->hDllInstance, LANG_DLL_GUID_STRING_ID, s, 39))
  604. {
  605. templng->external = 1;
  606. templng->guidstr = _wcsdup(s);
  607. GetImageHashData(templng->hDllInstance);
  608. }
  609. // only keep if it's a valid lng dll ie doesn't have load issues
  610. lnglist.push_back(templng);
  611. }
  612. }
  613. while (FindNextFileW(h, &find_data));
  614. FindClose(h);
  615. }
  616. }
  617. }
  618. return is_wlz;
  619. }
  620. // return 1 if we're working from a wlz otherwise return 0
  621. int extract_wlz_to_dir(wchar_t* readme_only_wlz_extraction, BOOL *skip)
  622. {
  623. int is_wlz = 0;
  624. if (config_langpack[0] || readme_only_wlz_extraction && readme_only_wlz_extraction[0])
  625. {
  626. wchar_t* langpack = (readme_only_wlz_extraction?readme_only_wlz_extraction:config_langpack),
  627. tempdirbuf[MAX_PATH] = {0}, *TEMPDIR = LANGTEMPDIR;
  628. if (_wcsicmp(extensionW(langpack), L"zip") && _wcsicmp(extensionW(langpack), L"wlz"))
  629. {
  630. if (PathIsFileSpecW(langpack) || PathIsRelativeW(langpack))
  631. PathCombineW(lang_directory, LANGDIR, langpack);
  632. else
  633. StringCchCopyW(lang_directory, MAX_PATH, langpack);
  634. is_wlz = load_extra_lng(TRUE);
  635. if (skip) *skip = is_wlz;
  636. }
  637. else
  638. {
  639. wchar_t dirmask[MAX_PATH*4] = {0};
  640. char str[78] = {0};
  641. unzFile f = {0};
  642. // make sure that we use a different folder from the current wlz temp folder otherwise we have issues
  643. if(readme_only_wlz_extraction){
  644. wchar_t buf[MAX_PATH] = {0};
  645. GetTempPathW(MAX_PATH, buf);
  646. GetTempFileNameW(buf, L"WLZ", GetTickCount(), tempdirbuf);
  647. TEMPDIR = tempdirbuf;
  648. }
  649. CreateDirectoryW(TEMPDIR, NULL);
  650. StringCchPrintfA(str,78,"lang_clean_up%ws",szAppName);
  651. if(!readme_only_wlz_extraction){
  652. StringCchCopyW(lang_directory, MAX_PATH, TEMPDIR);
  653. _w_sW(str, TEMPDIR);
  654. }
  655. if (PathIsFileSpecW(langpack)|| PathIsRelativeW(langpack))
  656. PathCombineW(dirmask, LANGDIR, langpack);
  657. else
  658. StringCchCopyW(dirmask, MAX_PATH*4, langpack);
  659. // now we're going to extract, if doing a temp extraction then set the path into the passed buffer
  660. if(readme_only_wlz_extraction){
  661. StringCchCopyW(readme_only_wlz_extraction, MAX_PATH, TEMPDIR);
  662. }
  663. f = unzOpen(AutoCharFn(dirmask));
  664. if (f)
  665. {
  666. if (unzGoToFirstFile(f) == UNZ_OK)
  667. {
  668. OVERLAPPED asyncIO = {0};
  669. int isNT = (GetVersion() < 0x80000000);
  670. if (isNT)
  671. {
  672. asyncIO.hEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
  673. asyncIO.OffsetHigh = 0;
  674. }
  675. do
  676. {
  677. char filename[MAX_PATH] = {0}, *fn = 0, *p = 0;
  678. if (isNT)
  679. SetEvent(asyncIO.hEvent);
  680. unzGetCurrentFileInfo(f, NULL, filename, sizeof(filename), NULL, 0, NULL, 0);
  681. //Only extract the file-types that could be in a skin
  682. //If we don't filter here it's a security hole
  683. // expand out folders if we've got a freeform based folder
  684. if(!_strnicmp(filename,"freeform\\",9) || !_strnicmp(filename,"freeform/",9))
  685. fn = filename;
  686. // otherwise just extract to the root of the temp directory
  687. else
  688. fn = scanstr_back(filename, "\\/", filename - 1) + 1;
  689. p = extension(fn);
  690. // TODO: really should enum image loaders so we only extract supported image files
  691. if (!_stricmp(p, "lng") || !_stricmp(p, "ini") || !_stricmp(p, "txt") ||
  692. !_stricmp(p, "png") || !_stricmp(p, "bmp") || !_stricmp(p, "gif") ||
  693. !_stricmp(p, "jpg") || !_stricmp(p, "xml") || !_stricmp(p, "htm") ||
  694. // not too keen on dll in there but that's how the GN dlls are named
  695. !_stricmp(p, "dll"))
  696. {
  697. if (unzOpenCurrentFile(f) == UNZ_OK)
  698. {
  699. PathCombineW(dirmask, TEMPDIR, AutoWide(fn));
  700. CreateDirectoryForFileW(dirmask, TEMPDIR);
  701. HANDLE fp = CreateFileW(dirmask, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | (isNT ? FILE_FLAG_OVERLAPPED : 0), NULL);
  702. if (fp != INVALID_HANDLE_VALUE)
  703. {
  704. int l = 0, pos = 0, bufNum=0;
  705. #define LANG_ZIP_BUFFER_SIZE 2048
  706. char buf[LANG_ZIP_BUFFER_SIZE*2] = {0};
  707. int success = 1;
  708. do
  709. {
  710. DWORD written = 0;
  711. bufNum = !bufNum;
  712. l = unzReadCurrentFile(f, buf+LANG_ZIP_BUFFER_SIZE*bufNum, LANG_ZIP_BUFFER_SIZE);
  713. if (!l)
  714. unzCloseCurrentFile(f);
  715. if (isNT)
  716. {
  717. WaitForSingleObject(asyncIO.hEvent, INFINITE);
  718. if (l > 0)
  719. {
  720. asyncIO.Offset = pos;
  721. if (WriteFile(fp, buf+LANG_ZIP_BUFFER_SIZE*bufNum, l, NULL, &asyncIO) == FALSE
  722. && GetLastError() != ERROR_IO_PENDING)
  723. {
  724. success=0;
  725. }
  726. pos += l;
  727. }
  728. }
  729. else
  730. {
  731. if (l > 0)
  732. {
  733. if (WriteFile(fp, buf+LANG_ZIP_BUFFER_SIZE*bufNum, l, &written, NULL) == FALSE)
  734. success = 0;
  735. }
  736. }
  737. } while (l > 0 && success);
  738. CloseHandle(fp);
  739. // cache information about the extracted lng files
  740. if(!_stricmp(p, "lng") && !readme_only_wlz_extraction)
  741. {
  742. is_wlz = 1;
  743. winampLangStruct* templng = reinterpret_cast<winampLangStruct*>(calloc(1, sizeof(winampLangStruct)));
  744. templng->module = AutoWideDup(filename);
  745. bool exception = (!lstrcmpiW(templng->module, L"omBrowser.lng") || !lstrcmpiW(templng->module, L"ml_online.lng"));
  746. // the plain LoadLibrary(..) generally works though as we only want to
  747. // load the lng files for resources (and that some people's lng files
  748. // can generally end up being corrupted after a few edits), we instead
  749. // try to load as an image / data file (so doesn't re-map things, etc)
  750. templng->hDllInstance = LoadLibraryExW(dirmask, NULL, (!exception ? LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE : 0));
  751. if (!templng->hDllInstance) templng->hDllInstance = LoadLibraryW(dirmask);
  752. if (templng->hDllInstance)
  753. {
  754. wchar_t s[39] = {0};
  755. if(LoadStringW(templng->hDllInstance, LANG_DLL_GUID_STRING_ID, s, 39))
  756. {
  757. templng->guidstr = _wcsdup(s);
  758. GetImageHashData(templng->hDllInstance);
  759. }
  760. // only keep if it's a valid lng dll ie doesn't have load issues
  761. lnglist.push_back(templng);
  762. }
  763. }
  764. }
  765. }
  766. }
  767. }
  768. while (unzGoToNextFile(f) == UNZ_OK);
  769. if (isNT && asyncIO.hEvent)
  770. {
  771. CloseHandle(asyncIO.hEvent);
  772. }
  773. }
  774. unzClose(f);
  775. }
  776. }
  777. }
  778. else lang_directory[0] = 0;
  779. return is_wlz;
  780. }
  781. HINSTANCE Language::FindDllHandleByGUID(const GUID guid)
  782. {
  783. wchar_t gs[40] = {0};
  784. getGUIDstr(guid,gs);
  785. for ( winampLangStruct *l_lng : lnglist )
  786. {
  787. if( l_lng->guidstr && *l_lng->guidstr && !_wcsnicmp(gs, l_lng->guidstr, 38))
  788. return l_lng->hDllInstance;
  789. }
  790. return NULL;
  791. }
  792. HINSTANCE Language::FindDllHandleByString(const char* _str)
  793. {
  794. AutoWide str__(_str);
  795. const wchar_t *str = str__;
  796. if(str && *str)
  797. {
  798. for ( winampLangStruct *l_lng : lnglist )
  799. {
  800. if ( l_lng->module && *l_lng->module && !_wcsnicmp( l_lng->module, str, lstrlenW( str ) ) )
  801. return l_lng->hDllInstance;
  802. }
  803. }
  804. return NULL;
  805. }
  806. HINSTANCE Language::FindDllHandleByStringW(const wchar_t* _str)
  807. {
  808. AutoChar str__(_str);
  809. const wchar_t *str = _str;
  810. if(str && *str)
  811. {
  812. for ( winampLangStruct *l_lng : lnglist )
  813. {
  814. if( l_lng->module && *l_lng->module && !_wcsnicmp( l_lng->module, str, lstrlenW(str)))
  815. return l_lng->hDllInstance;
  816. }
  817. }
  818. return NULL;
  819. }
  820. HINSTANCE Lang_InitLangSupport(HINSTANCE hinst, const GUID guid)
  821. {
  822. geno = _r_i("geno", 1);
  823. started = 1;
  824. return langManager->StartLanguageSupport(hinst, guid);
  825. }
  826. void Lang_FollowUserDecimalLocale(void)
  827. {
  828. langManager->UseUserNumericLocale();
  829. }
  830. // use this to load based on the module specified so that we make sure
  831. // we've got the correct hinstance based on lng file or default handle
  832. HINSTANCE Language::StartLanguageSupport(HINSTANCE hinstance, const GUID guid)
  833. {
  834. if (!g_safeMode)
  835. {
  836. HWND agent = FindWindowW(L"WinampAgentMain", NULL);
  837. wchar_t winampaLngPath[MAX_PATH] = {0};
  838. int is_wlz = 0;
  839. // if we find Winamp Agent running then we need to tell it
  840. // to unload it's winampa.lng for what we're about to do..
  841. if (IsWindow(agent) && !already_extracted)
  842. {
  843. SendMessageW(agent, WM_USER + 16, 1, 0);
  844. }
  845. // always remove winampa.lng just incase we crashed and it leaves things out of synch
  846. if(!already_extracted){
  847. StringCchPrintfW(winampaLngPath, MAX_PATH, L"%s\\winampa.lng", CONFIGDIR);
  848. DeleteFileW(winampaLngPath);
  849. }
  850. config_load_langpack_var();
  851. if(!already_extracted)
  852. {
  853. BOOL skip = FALSE;
  854. already_extracted = 1;
  855. prev_wlz_ex_state = is_wlz = extract_wlz_to_dir(0, &skip);
  856. if (!skip) load_extra_lng(FALSE);
  857. else LANGTEMPDIR[0] = 0;
  858. }
  859. else
  860. {
  861. is_wlz = prev_wlz_ex_state;
  862. agent = 0;
  863. }
  864. // make sure that we don't try and load the exe/dll being localised as the lng dll
  865. wchar_t modulename[MAX_PATH] = {0}, *p = 0;
  866. GetModuleFileNameW(hinstance, modulename, MAX_PATH);
  867. p = scanstr_backW(modulename, L"\\/", NULL);
  868. if(p) p = CharNextW(p);
  869. // if is_wlz != 0 then we can attempt to use the wlz extracted files otherwise
  870. // (for the time being) we drop back to the older lng pack system
  871. // either way we still need to make sure that what we're using is valid
  872. if (config_langpack[0] && is_wlz)
  873. {
  874. HMODULE h = langManager->FindDllHandleByGUID(guid);
  875. if(!h) // possible fallback usage if things failed to work on guid look up
  876. { // though wouldn't be reliable if people change the lng file names
  877. wchar_t tmpfile[MAX_PATH], *t = 0;
  878. lstrcpynW(tmpfile,p,MAX_PATH);
  879. t = scanstr_backW(tmpfile, L".", NULL);
  880. lstrcpynW(t,L".lng",MAX_PATH);
  881. h = langManager->FindDllHandleByStringW(tmpfile);
  882. }
  883. if (h)
  884. {
  885. // if the wlz was able to be loaded (as we believe at this point)
  886. // then we see if Winamp Agent is running and tell it to refresh
  887. // it's version of winampa.lng once we've copied into %inidir%
  888. if (IsWindow(agent))
  889. {
  890. // copy from the wlz folder to the settings folder
  891. wchar_t winampaWlzPath[MAX_PATH] = {0};
  892. StringCchPrintfW(winampaWlzPath, MAX_PATH, L"%s\\winampa.lng", lang_directory);
  893. CopyFileW(winampaWlzPath,winampaLngPath,FALSE);
  894. SendMessageW(agent, WM_USER + 16, 0, 0);
  895. }
  896. // if we get here then we've managed to load the language pack
  897. // (still could be invalid but that's generally from failed dll files)
  898. return h;
  899. }
  900. }
  901. }
  902. // make sure we return the passed hinstance incase of failure to load/invalid lng file/etc
  903. return hinstance;
  904. }
  905. void Lang_EndLangSupport(void)
  906. {
  907. started = 0;
  908. // need to fully clean up things here including unloading of the langpack
  909. HINSTANCE old_language_pack_instance = language_pack_instance;
  910. if(language_pack_instance != hMainInstance)
  911. {
  912. FreeLibrary(language_pack_instance);
  913. language_pack_instance = hMainInstance;
  914. }
  915. for ( winampLangStruct *l_lng : lnglist )
  916. {
  917. if( l_lng->module)
  918. {
  919. free( l_lng->module);
  920. l_lng->module = 0;
  921. }
  922. if( l_lng->guidstr)
  923. {
  924. free( l_lng->guidstr);
  925. l_lng->guidstr = 0;
  926. }
  927. // this check is to prevent trying to re-free the winamp.lng (as it's done earlier)
  928. // as well as anything which is not in the temp folder to avoid any unloading issues
  929. if ( !l_lng->external && l_lng->hDllInstance && ( l_lng->hDllInstance != old_language_pack_instance ) )
  930. {
  931. FreeLibrary( l_lng->hDllInstance );
  932. l_lng->hDllInstance = 0;
  933. }
  934. }
  935. lnglist.clear();
  936. prev_wlz_ex_state = already_extracted = 0;
  937. }
  938. HINSTANCE Lang_FakeWinampLangHInst(HINSTANCE adjustedHInst){
  939. HINSTANCE previousHInst = language_pack_instance;
  940. language_pack_instance = adjustedHInst;
  941. started = !!adjustedHInst;
  942. return previousHInst;
  943. }
  944. void Lang_LocaliseAgentOnTheFly(BOOL refresh){
  945. // if we need to refresh then attempt to use the winampa.lng from the
  946. // current language pack if one is present and has been extracted so
  947. // we test to see if we've extracted a language pack already
  948. if(already_extracted){
  949. HWND agent = FindWindowW(L"WinampAgentMain", NULL);
  950. wchar_t winampaLngPath[MAX_PATH] = {0};
  951. // if we find Winamp Agent running then we need to tell it
  952. // to unload it's winampa.lng for what we're about to do...
  953. // although this is likely to be a new load, doing this will
  954. // help to ensure that things are unloaded incase of issues
  955. if(IsWindow(agent)){
  956. SendMessageW(agent, WM_USER + 16, 1, 0);
  957. }
  958. // always remove winampa.lng just incase we crashed and it leaves things out of synch
  959. StringCchPrintfW(winampaLngPath, MAX_PATH, L"%s\\winampa.lng", CONFIGDIR);
  960. DeleteFileW(winampaLngPath);
  961. if(refresh){
  962. wchar_t winampaWlzPath[MAX_PATH] = {0};
  963. StringCchPrintfW(winampaWlzPath, MAX_PATH, L"%s\\winampa.lng", lang_directory);
  964. CopyFileW(winampaWlzPath,winampaLngPath,FALSE);
  965. SendMessageW(agent, WM_USER + 16, 0, 0);
  966. }
  967. }
  968. }
  969. #ifdef CBCLASS
  970. #undef CBCLASS
  971. #endif
  972. #define CBCLASS Language
  973. START_DISPATCH;
  974. CB( API_LANGUAGE_GETSTRING, GetString )
  975. CB( API_LANGUAGE_GETSTRINGW, GetStringW )
  976. CB( API_LANGUAGE_GETSTRINGFROMGUID, GetStringFromGUID )
  977. CB( API_LANGUAGE_GETSTRINGFROMGUIDW, GetStringFromGUIDW )
  978. CB( API_LANGUAGE_GETHINSTANCEBYGUID, FindDllHandleByGUID )
  979. CB( API_LANGUAGE_GETHINSTANCEBYNAME, FindDllHandleByString )
  980. CB( API_LANGUAGE_GETHINSTANCEBYNAMEW, FindDllHandleByStringW )
  981. CB( API_LANGUAGE_STARTUP, StartLanguageSupport )
  982. CB( API_LANGUAGE_GETLANGUAGEFOLDER, GetLanguageFolder )
  983. CB( API_LANGUAGE_CREATELDIALOGPARAM, CreateLDialogParam )
  984. CB( API_LANGUAGE_LDIALOGBOXPARAM, LDialogBoxParam )
  985. CB( API_LANGUAGE_LOADLMENU, LoadLMenu )
  986. CB( API_LANGUAGE_CREATELDIALOGPARAMW, CreateLDialogParamW )
  987. CB( API_LANGUAGE_LDIALOGBOXPARAMW, LDialogBoxParamW )
  988. CB( API_LANGUAGE_LOADLMENUW, LoadLMenuW )
  989. CB( API_LANGUAGE_GETLANGUAGEIDENTIFIER, GetLanguageIdentifier )
  990. CB( API_LANGUAGE_LOADRESOURCEFROMFILEA, LoadResourceFromFile )
  991. CB( API_LANGUAGE_LOADRESOURCEFROMFILEW, LoadResourceFromFileW )
  992. CB( API_LANGUAGE_LOADACCELERATORSA, LoadAcceleratorsA )
  993. CB( API_LANGUAGE_LOADACCELERATORSW, LoadAcceleratorsW )
  994. CB( API_LANGUAGE_USEUSERNUMERICLOCALE, UseUserNumericLocale )
  995. CB( API_LANGUAGE_GET_C_NUMERICLOCALE, Get_C_NumericLocale )
  996. CB( API_LANGUAGE_FORMATTEDSIZESTRING, FormattedSizeString )
  997. END_DISPATCH