xref: /reactos/dll/win32/mlang/mlang.c (revision 19b18ce2)
1 /*
2  *    MLANG Class Factory
3  *
4  * Copyright 2002 Lionel Ulmer
5  * Copyright 2003,2004 Mike McCormack
6  * Copyright 2004,2005 Dmitry Timoshkov
7  * Copyright 2009 Detlef Riekenberg
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #include "config.h"
25 
26 #include <stdarg.h>
27 #include <stdio.h>
28 
29 #define COBJMACROS
30 
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wingdi.h"
34 #include "winuser.h"
35 #include "ole2.h"
36 #include "objbase.h"
37 #include "rpcproxy.h"
38 #include "mlang.h"
39 #include "mimeole.h"
40 
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
43 #include "wine/list.h"
44 
45 WINE_DEFAULT_DEBUG_CHANNEL(mlang);
46 
47 #include "initguid.h"
48 
49 static HRESULT MultiLanguage_create(IUnknown *pUnkOuter, LPVOID *ppObj);
50 static HRESULT MLangConvertCharset_create(IUnknown *outer, void **obj);
51 static HRESULT EnumRfc1766_create(LANGID LangId, IEnumRfc1766 **ppEnum);
52 
53 static HINSTANCE instance;
54 static DWORD MLANG_tls_index; /* to store various per thead data */
55 
56 /* FIXME:
57  * Under what circumstances HKEY_CLASSES_ROOT\MIME\Database\Codepage and
58  * HKEY_CLASSES_ROOT\MIME\Database\Charset are used?
59  */
60 
61 typedef struct
62 {
63     const char *description;
64     UINT cp;
65     DWORD flags;
66     const char *web_charset;
67     const char *header_charset;
68     const char *body_charset;
69     const WCHAR *alias;
70 } MIME_CP_INFO;
71 
72 /* These data are based on the codepage info in libs/unicode/cpmap.pl */
73 /* FIXME: Add 28604 (Celtic), 28606 (Balkan) */
74 
75 static const MIME_CP_INFO arabic_cp[] =
76 {
77     { "Arabic (864)",
78       864, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
79            MIMECONTF_MIME_LATEST,
80       "ibm864", "ibm864", "ibm864" },
81     { "Arabic (1006)",
82       1006, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
83             MIMECONTF_MIME_LATEST,
84       "ibm1006", "ibm1006", "ibm1006" },
85     { "Arabic (Windows)",
86       1256, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
87             MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
88             MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
89       "windows-1256", "windows-1256", "windows-1256" },
90     { "Arabic (ISO)",
91       28596, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
92              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
93              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
94              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
95       "iso-8859-6", "iso-8859-6", "iso-8859-6" }
96 };
97 static const MIME_CP_INFO baltic_cp[] =
98 {
99     { "Baltic (DOS)",
100       775, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
101            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
102       "ibm775", "ibm775", "ibm775" },
103     { "Baltic (Windows)",
104       1257, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
105             MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
106             MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
107             MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
108       "windows-1257", "windows-1257", "windows-1257" },
109     { "Baltic (ISO)",
110       28594, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
111              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
112              MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
113              MIMECONTF_MIME_LATEST,
114       "iso-8859-4", "iso-8859-4", "iso-8859-4" },
115     { "Estonian (ISO)",
116       28603, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
117              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
118       "iso-8859-13", "iso-8859-13", "iso-8859-13" }
119 };
120 static const MIME_CP_INFO chinese_simplified_cp[] =
121 {
122     { "Chinese Simplified (Auto-Select)",
123       50936, MIMECONTF_IMPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
124              MIMECONTF_MIME_LATEST,
125       "_autodetect_chs", "_autodetect_chs", "_autodetect_chs" },
126     { "Chinese Simplified (GB2312)",
127       936, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
128            MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_VALID |
129            MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
130            MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
131       "gb2312", "gb2312", "gb2312" },
132     { "Chinese Simplified (GB2312-80)",
133       20936, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
134              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
135       "x-cp20936", "x-cp20936", "x-cp20936" },
136     { "Chinese Simplified (HZ)",
137       52936, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
138              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
139              MIMECONTF_VALID | MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 |
140              MIMECONTF_MIME_LATEST,
141       "hz-gb-2312", "hz-gb-2312", "hz-gb-2312" },
142     { "Chinese Simplified (GB18030)",
143       54936, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
144              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
145              MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
146              MIMECONTF_MIME_LATEST,
147       "GB18030", "GB18030", "GB18030" },
148     { "Chinese Simplified (GBK)",
149       936, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
150            MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
151            MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
152            MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
153       "gbk", "gbk", "gbk" }
154 };
155 static const MIME_CP_INFO chinese_traditional_cp[] =
156 {
157     { "Chinese Traditional (Auto-Select)",
158       50950, MIMECONTF_IMPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
159              MIMECONTF_MIME_LATEST,
160       "_autodetect_cht", "_autodetect_cht", "_autodetect_cht" },
161     { "Chinese Traditional (Big5)",
162       950, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
163            MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
164            MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
165            MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
166       "big5", "big5", "big5" },
167     { "Chinese Traditional (CNS)",
168       20000, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
169              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
170       "x-Chinese-CNS", "x-Chinese-CNS", "x-Chinese-CNS" }
171 };
172 static const MIME_CP_INFO central_european_cp[] =
173 {
174     { "Central European (DOS)",
175       852, MIMECONTF_BROWSER | MIMECONTF_IMPORT | MIMECONTF_SAVABLE_BROWSER |
176            MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
177            MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
178       "ibm852", "ibm852", "ibm852" },
179     { "Central European (Windows)",
180       1250, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
181             MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
182             MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
183             MIMECONTF_MIME_LATEST,
184       "windows-1250", "windows-1250", "windows-1250" },
185     { "Central European (Mac)",
186       10029, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
187              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
188       "x-mac-ce", "x-mac-ce", "x-mac-ce" },
189     { "Central European (ISO)",
190       28592, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
191              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
192              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
193              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
194       "iso-8859-2", "iso-8859-2", "iso-8859-2" }
195 };
196 static const MIME_CP_INFO cyrillic_cp[] =
197 {
198     { "OEM Cyrillic",
199       855, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
200            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
201       "ibm855", "ibm855", "ibm855" },
202     { "Cyrillic (DOS)",
203       866, MIMECONTF_BROWSER | MIMECONTF_IMPORT | MIMECONTF_SAVABLE_BROWSER |
204            MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 |
205            MIMECONTF_MIME_LATEST,
206       "cp866", "cp866", "cp866" },
207 #if 0 /* Windows has 20866 as an official code page for KOI8-R */
208     { "Cyrillic (KOI8-R)",
209       878, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
210            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
211       "koi8-r", "koi8-r", "koi8-r" },
212 #endif
213     { "Cyrillic (Windows)",
214       1251, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
215             MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
216             MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
217       "windows-1251", "windows-1251", "windows-1251" },
218     { "Cyrillic (Mac)",
219       10007, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
220              MIMECONTF_MIME_LATEST,
221       "x-mac-cyrillic", "x-mac-cyrillic", "x-mac-cyrillic" },
222     { "Cyrillic (KOI8-R)",
223       20866, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
224              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
225              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
226              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
227       "koi8-r", "koi8-r", "koi8-r" },
228     { "Cyrillic (KOI8-U)",
229       21866, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
230              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
231              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
232              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
233       "koi8-u", "koi8-u", "koi8-u" },
234     { "Cyrillic (ISO)",
235       28595, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
236              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
237              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
238              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
239       "iso-8859-5", "iso-8859-5", "iso-8859-5" }
240 };
241 static const MIME_CP_INFO greek_cp[] =
242 {
243     { "Greek (DOS)",
244       737, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
245            MIMECONTF_MIME_LATEST,
246       "ibm737", "ibm737", "ibm737" },
247     { "Greek, Modern (DOS)",
248       869, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
249            MIMECONTF_MIME_LATEST,
250       "ibm869", "ibm869", "ibm869" },
251     { "IBM EBCDIC (Greek Modern)",
252       875, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
253            MIMECONTF_MIME_LATEST,
254       "cp875", "cp875", "cp875" },
255     { "Greek (Windows)",
256       1253, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
257             MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
258             MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
259       "windows-1253", "windows-1253", "windows-1253" },
260     { "Greek (Mac)",
261       10006, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
262              MIMECONTF_MIME_LATEST,
263       "x-mac-greek", "x-mac-greek", "x-mac-greek" },
264     { "Greek (ISO)",
265       28597, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
266              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
267              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
268              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
269       "iso-8859-7", "iso-8859-7", "iso-8859-7" }
270 };
271 static const MIME_CP_INFO hebrew_cp[] =
272 {
273     { "Hebrew (424)",
274       424, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
275            MIMECONTF_MIME_LATEST,
276       "ibm424", "ibm424", "ibm424" },
277     { "Hebrew (856)",
278       856, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
279            MIMECONTF_MIME_LATEST,
280       "cp856", "cp856", "cp856" },
281     { "Hebrew (DOS)",
282       862, MIMECONTF_BROWSER | MIMECONTF_MINIMAL | MIMECONTF_IMPORT |
283            MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
284            MIMECONTF_MIME_LATEST,
285       "dos-862", "dos-862", "dos-862" },
286     { "Hebrew (Windows)",
287       1255, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
288             MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
289             MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
290       "windows-1255", "windows-1255", "windows-1255" },
291     { "Hebrew (ISO-Visual)",
292       28598, MIMECONTF_BROWSER | MIMECONTF_MINIMAL | MIMECONTF_IMPORT |
293              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
294              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
295       "iso-8859-8", "iso-8859-8", "iso-8859-8" }
296 };
297 static const MIME_CP_INFO japanese_cp[] =
298 {
299     { "Japanese (Auto-Select)",
300       50932, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
301              MIMECONTF_IMPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
302              MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
303       "_autodetect", "_autodetect", "_autodetect" },
304     { "Japanese (EUC)",
305       51932, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
306              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
307              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
308              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
309       "euc-jp", "euc-jp", "euc-jp" },
310     { "Japanese (JIS)",
311       50220, MIMECONTF_IMPORT | MIMECONTF_MAILNEWS | MIMECONTF_EXPORT |
312              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_VALID_NLS |
313              MIMECONTF_PRIVCONVERTER | MIMECONTF_MIME_LATEST |
314              MIMECONTF_MIME_IE4,
315       "iso-2022-jp","iso-2022-jp","iso-2022-jp"},
316     { "Japanese (JIS 0208-1990 and 0212-1990)",
317       20932, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
318              MIMECONTF_VALID | MIMECONTF_PRIVCONVERTER | MIMECONTF_MIME_LATEST,
319       "EUC-JP","EUC-JP","EUC-JP"},
320     { "Japanese (JIS-Allow 1 byte Kana)",
321       50221, MIMECONTF_MAILNEWS | MIMECONTF_EXPORT | MIMECONTF_SAVABLE_BROWSER |
322              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_VALID_NLS |
323              MIMECONTF_VALID | MIMECONTF_PRIVCONVERTER | MIMECONTF_MIME_LATEST,
324       "csISO2022JP","iso-2022-jp","iso-2022-jp"},
325     { "Japanese (JIS-Allow 1 byte Kana - SO/SI)",
326       50222, MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_VALID |
327              MIMECONTF_PRIVCONVERTER | MIMECONTF_MIME_LATEST,
328       "iso-2022-jp","iso-2022-jp","iso-2022-jp"},
329     { "Japanese (Mac)",
330       10001, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
331              MIMECONTF_VALID | MIMECONTF_PRIVCONVERTER | MIMECONTF_MIME_LATEST,
332       "x-mac-japanese","x-mac-japanese","x-mac-japanese"},
333     { "Japanese (Shift-JIS)",
334       932, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
335            MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
336            MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
337            MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
338       "shift_jis", "iso-2022-jp", "iso-2022-jp" }
339 };
340 static const MIME_CP_INFO korean_cp[] =
341 {
342     { "Korean",
343       949, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
344            MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
345            MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID_NLS |
346            MIMECONTF_MIME_LATEST,
347       "ks_c_5601-1987", "ks_c_5601-1987", "ks_c_5601-1987" }
348 };
349 static const MIME_CP_INFO thai_cp[] =
350 {
351     { "Thai (Windows)",
352       874, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_MIME_LATEST,
353       "ibm-thai", "ibm-thai", "ibm-thai" }
354 };
355 static const MIME_CP_INFO turkish_cp[] =
356 {
357     { "Turkish (DOS)",
358       857, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
359            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
360       "ibm857", "ibm857", "ibm857" },
361     { "IBM EBCDIC (Turkish Latin-5)",
362       1026, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
363             MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
364       "ibm1026", "ibm1026", "ibm1026" },
365     { "Turkish (Windows)",
366       1254, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
367             MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
368             MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
369             MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
370       "windows-1254", "windows-1254", "windows-1254" },
371     { "Turkish (Mac)",
372       10081, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
373              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
374       "x-mac-turkish", "x-mac-turkish", "x-mac-turkish" },
375     { "Latin 3 (ISO)",
376       28593, MIMECONTF_MAILNEWS | MIMECONTF_IMPORT |
377              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_EXPORT | MIMECONTF_VALID |
378              MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
379       "iso-8859-3", "iso-8859-3", "iso-8859-3" },
380     { "Turkish (ISO)",
381       28599, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
382              MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
383              MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
384              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
385       "iso-8859-9", "iso-8859-9", "iso-8859-9" }
386 };
387 static const MIME_CP_INFO vietnamese_cp[] =
388 {
389     { "Vietnamese (Windows)",
390       1258, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
391             MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
392             MIMECONTF_EXPORT | MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 |
393             MIMECONTF_MIME_LATEST,
394       "windows-1258", "windows-1258", "windows-1258" }
395 };
396 
397 static const WCHAR asciiW[] = {'a','s','c','i','i',0};
398 
399 static const MIME_CP_INFO western_cp[] =
400 {
401     { "IBM EBCDIC (US-Canada)",
402       37, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
403           MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
404       "ibm037", "ibm037", "ibm037" },
405     { "OEM United States",
406       437, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
407            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
408       "ibm437", "ibm437", "ibm437" },
409     { "IBM EBCDIC (International)",
410       500, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
411            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
412       "ibm500", "ibm500", "ibm500" },
413     { "Western European (DOS)",
414       850, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
415            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
416       "ibm850", "ibm850", "ibm850" },
417     { "Portuguese (DOS)",
418       860, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
419            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
420       "ibm860", "ibm860", "ibm860" },
421     { "Icelandic (DOS)",
422       861, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
423            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
424       "ibm861", "ibm861", "ibm861" },
425     { "French Canadian (DOS)",
426       863, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
427            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
428       "ibm863", "ibm863", "ibm863" },
429     { "Nordic (DOS)",
430       865, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
431            MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
432       "ibm865", "ibm865", "ibm865" },
433     { "Western European (Windows)",
434       1252, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_MINIMAL |
435             MIMECONTF_IMPORT | MIMECONTF_SAVABLE_MAILNEWS |
436             MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT | MIMECONTF_VALID |
437             MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
438       "windows-1252", "windows-1252", "iso-8859-1" },
439     { "Western European (Mac)",
440       10000, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
441              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
442       "macintosh", "macintosh", "macintosh" },
443     { "Icelandic (Mac)",
444       10079, MIMECONTF_IMPORT | MIMECONTF_EXPORT | MIMECONTF_VALID |
445              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
446       "x-mac-icelandic", "x-mac-icelandic", "x-mac-icelandic" },
447     { "US-ASCII",
448       20127, MIMECONTF_MAILNEWS | MIMECONTF_IMPORT | MIMECONTF_EXPORT |
449              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_VALID |
450              MIMECONTF_VALID_NLS | MIMECONTF_MIME_LATEST,
451       "us-ascii", "us-ascii", "us-ascii", asciiW },
452     { "Western European (ISO)",
453       28591, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
454              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
455              MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
456              MIMECONTF_MIME_LATEST,
457       "iso-8859-1", "iso-8859-1", "iso-8859-1" },
458     { "Latin 9 (ISO)",
459       28605, MIMECONTF_MAILNEWS | MIMECONTF_IMPORT |
460              MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
461              MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
462              MIMECONTF_MIME_LATEST,
463       "iso-8859-15", "iso-8859-15", "iso-8859-15" }
464 };
465 static const MIME_CP_INFO unicode_cp[] =
466 {
467     { "Unicode",
468       CP_UNICODE, MIMECONTF_MINIMAL | MIMECONTF_IMPORT |
469                   MIMECONTF_SAVABLE_BROWSER | MIMECONTF_EXPORT |
470                   MIMECONTF_VALID | MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 |
471                   MIMECONTF_MIME_LATEST,
472       "unicode", "unicode", "unicode" },
473     { "Unicode (UTF-7)",
474       CP_UTF7, MIMECONTF_MAILNEWS | MIMECONTF_IMPORT |
475                MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_EXPORT | MIMECONTF_VALID |
476                MIMECONTF_VALID_NLS | MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
477       "utf-7", "utf-7", "utf-7" },
478     { "Unicode (UTF-8)",
479       CP_UTF8, MIMECONTF_MAILNEWS | MIMECONTF_BROWSER | MIMECONTF_IMPORT |
480                MIMECONTF_SAVABLE_MAILNEWS | MIMECONTF_SAVABLE_BROWSER |
481                MIMECONTF_EXPORT | MIMECONTF_VALID | MIMECONTF_VALID_NLS |
482                MIMECONTF_MIME_IE4 | MIMECONTF_MIME_LATEST,
483       "utf-8", "utf-8", "utf-8" }
484 };
485 
486 static const struct mlang_data
487 {
488     const char *description;
489     UINT family_codepage;
490     UINT number_of_cp;
491     const MIME_CP_INFO *mime_cp_info;
492     const char *fixed_font;
493     const char *proportional_font;
494     SCRIPT_ID sid;
495 } mlang_data[] =
496 {
497     { "Arabic",1256,sizeof(arabic_cp)/sizeof(arabic_cp[0]),arabic_cp,
498       "Simplified Arabic Fixed","Simplified Arabic", sidArabic },
499     { "Baltic",1257,sizeof(baltic_cp)/sizeof(baltic_cp[0]),baltic_cp,
500       "Courier New","Arial", sidAsciiLatin },
501     { "Chinese Simplified",936,sizeof(chinese_simplified_cp)/sizeof(chinese_simplified_cp[0]),chinese_simplified_cp,
502       "Simsun","Simsun", sidHan },
503     { "Chinese Traditional",950,sizeof(chinese_traditional_cp)/sizeof(chinese_traditional_cp[0]),chinese_traditional_cp,
504       "MingLiu","New MingLiu", sidBopomofo },
505     { "Central European",1250,sizeof(central_european_cp)/sizeof(central_european_cp[0]),central_european_cp,
506       "Courier New","Arial", sidAsciiLatin },
507     { "Cyrillic",1251,sizeof(cyrillic_cp)/sizeof(cyrillic_cp[0]),cyrillic_cp,
508       "Courier New","Arial", sidCyrillic },
509     { "Greek",1253,sizeof(greek_cp)/sizeof(greek_cp[0]),greek_cp,
510       "Courier New","Arial", sidGreek },
511     { "Hebrew",1255,sizeof(hebrew_cp)/sizeof(hebrew_cp[0]),hebrew_cp,
512       "Miriam Fixed","David", sidHebrew },
513     { "Japanese",932,sizeof(japanese_cp)/sizeof(japanese_cp[0]),japanese_cp,
514       "MS Gothic","MS PGothic", sidKana },
515     { "Korean",949,sizeof(korean_cp)/sizeof(korean_cp[0]),korean_cp,
516       "GulimChe","Gulim", sidHangul },
517     { "Thai",874,sizeof(thai_cp)/sizeof(thai_cp[0]),thai_cp,
518       "Tahoma","Tahoma", sidThai },
519     { "Turkish",1254,sizeof(turkish_cp)/sizeof(turkish_cp[0]),turkish_cp,
520       "Courier New","Arial", sidAsciiLatin },
521     { "Vietnamese",1258,sizeof(vietnamese_cp)/sizeof(vietnamese_cp[0]),vietnamese_cp,
522       "Courier New","Arial", sidAsciiLatin },
523     { "Western European",1252,sizeof(western_cp)/sizeof(western_cp[0]),western_cp,
524       "Courier New","Arial", sidAsciiLatin },
525     { "Unicode",CP_UNICODE,sizeof(unicode_cp)/sizeof(unicode_cp[0]),unicode_cp,
526       "Courier New","Arial" }
527 };
528 
529 struct font_list
530 {
531     struct list list_entry;
532     HFONT base_font;
533     HFONT font;
534     UINT charset;
535 };
536 
537 static struct list font_cache = LIST_INIT(font_cache);
538 static CRITICAL_SECTION font_cache_critical;
539 static CRITICAL_SECTION_DEBUG font_cache_critical_debug =
540 {
541     0, 0, &font_cache_critical,
542     { &font_cache_critical_debug.ProcessLocksList, &font_cache_critical_debug.ProcessLocksList },
543     0, 0, { (DWORD_PTR)(__FILE__ ": font_cache_critical") }
544 };
545 static CRITICAL_SECTION font_cache_critical = { &font_cache_critical_debug, -1, 0, 0, 0, 0 };
546 
547 static void fill_cp_info(const struct mlang_data *ml_data, UINT index, MIMECPINFO *mime_cp_info);
548 
549 static LONG dll_count;
550 
551 /*
552  * Japanese Detection and Conversion Functions
553  */
554 
555 #define HANKATA(A)  ((A >= 161) && (A <= 223))
556 #define ISEUC(A)    ((A >= 161) && (A <= 254))
557 #define NOTEUC(A,B) (((A >= 129) && (A <= 159)) && ((B >= 64) && (B <= 160)))
558 #define SJIS1(A)    (((A >= 129) && (A <= 159)) || ((A >= 224) && (A <= 239)))
559 #define SJIS2(A)    ((A >= 64) && (A <= 252))
560 #define ISMARU(A)   ((A >= 202) && (A <= 206))
561 #define ISNIGORI(A) (((A >= 182) && (A <= 196)) || ((A >= 202) && (A <= 206)))
562 
563 static UINT DetectJapaneseCode(LPCSTR input, DWORD count)
564 {
565     UINT code = 0;
566     DWORD i = 0;
567     unsigned char c1,c2;
568 
569     while ((code == 0 || code == 51932) && i < count)
570     {
571         c1 = input[i];
572         if (c1 == 0x1b /* ESC */)
573         {
574             i++;
575             if (i >= count)
576                 return code;
577             c1 = input[i];
578             if (c1 == '$')
579             {
580                 i++;
581                 if (i >= count)
582                     return code;
583                 c1 = input[i];
584                 if (c1 =='B' || c1 == '@')
585                     code = 50220;
586             }
587             if (c1 == 'K')
588                 code = 50220;
589         }
590         else if (c1 >= 129)
591         {
592             i++;
593             if (i >= count)
594                 return code;
595             c2 = input[i];
596             if NOTEUC(c1,c2)
597                 code = 932;
598             else if (ISEUC(c1) && ISEUC(c2))
599                 code = 51932;
600             else if (((c1 == 142)) && HANKATA(c2))
601                 code = 51932;
602         }
603         i++;
604     }
605     return code;
606 }
607 
608 static inline void jis2sjis(unsigned char *p1, unsigned char *p2)
609 {
610     unsigned char c1 = *p1;
611     unsigned char c2 = *p2;
612     int row = c1 < 95 ? 112 : 176;
613     int cell = c1 % 2 ? 31 + (c2 > 95) : 126;
614 
615     *p1 = ((c1 + 1) >> 1) + row;
616     *p2 = c2 + cell;
617 }
618 
619 static inline void sjis2jis(unsigned char *p1, unsigned char *p2)
620 {
621     unsigned char c1 = *p1;
622     unsigned char c2 = *p2;
623     int shift = c2 < 159;
624     int row = c1 < 160 ? 112 : 176;
625     int cell = shift ? (31 + (c2 > 127)): 126;
626 
627     *p1 = ((c1 - row) << 1) - shift;
628     *p2 -= cell;
629 }
630 
631 static int han2zen(unsigned char *p1, unsigned char *p2)
632 {
633     BOOL maru = FALSE;
634     BOOL nigori = FALSE;
635     static const unsigned char char1[] = {129,129,129,129,129,131,131,131,131,
636         131,131,131,131,131,131,129,131,131,131,131,131,131,131,131,131,131,
637         131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,
638         131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,
639         131,129,129 };
640     static const unsigned char char2[] = {66,117,118,65,69,146,64,66,68,70,
641         72,131,133,135,98,91,65,67,69,71,73,74,76,78,80,82,84,86,88,90,92,94,
642         96,99,101,103,105,106,107,108,109,110,113,116,119,122,125,126,128,
643         129,130,132,134,136,137,138,139,140,141,143,147,74,75};
644 
645     if (( *p2 == 222) && ((ISNIGORI(*p1) || (*p1 == 179))))
646             nigori = TRUE;
647     else if ((*p2 == 223) && (ISMARU(*p1)))
648             maru = TRUE;
649 
650     if (*p1 >= 161 && *p1 <= 223)
651     {
652         unsigned char index = *p1 - 161;
653         *p1 = char1[index];
654         *p2 = char2[index];
655     }
656 
657     if (maru || nigori)
658     {
659         if (nigori)
660         {
661             if (((*p2 >= 74) && (*p2 <= 103)) || ((*p2 >= 110) && (*p2 <= 122)))
662                 (*p2)++;
663             else if ((*p1 == 131) && (*p2 == 69))
664                 *p2 = 148;
665         }
666         else if ((maru) && ((*p2 >= 110) && (*p2 <= 122)))
667             *p2+= 2;
668 
669         return 1;
670     }
671 
672     return 0;
673 }
674 
675 
676 static UINT ConvertJIS2SJIS(LPCSTR input, DWORD count, LPSTR output)
677 {
678     DWORD i = 0;
679     int j = 0;
680     unsigned char p2,p;
681     BOOL shifted = FALSE;
682 
683     while (i < count)
684     {
685         p = input[i];
686         if (p == 0x1b /* ESC */)
687         {
688             i++;
689             if (i >= count)
690                 return 0;
691             p2 = input[i];
692             if (p2 == '$' || p2 =='(')
693                 i++;
694             if (p2 == 'K' || p2 =='$')
695                 shifted = TRUE;
696             else
697                 shifted = FALSE;
698         }
699         else
700         {
701             if (shifted)
702             {
703                 i++;
704                 if (i >= count)
705                     return 0;
706                 p2 = input[i];
707                 jis2sjis(&p,&p2);
708                 output[j++]=p;
709                 output[j++]=p2;
710             }
711             else
712             {
713                 output[j++] = p;
714             }
715         }
716         i++;
717     }
718     return j;
719 }
720 
721 static inline int exit_shift(LPSTR out, int c)
722 {
723     if (out)
724     {
725         out[c] = 0x1b;
726         out[c+1] = '(';
727         out[c+2] = 'B';
728     }
729     return 3;
730 }
731 
732 static inline int enter_shift(LPSTR out, int c)
733 {
734     if (out)
735     {
736         out[c] = 0x1b;
737         out[c+1] = '$';
738         out[c+2] = 'B';
739     }
740     return 3;
741 }
742 
743 
744 static UINT ConvertSJIS2JIS(LPCSTR input, DWORD count, LPSTR output)
745 {
746     DWORD i = 0;
747     int j = 0;
748     unsigned char p2,p;
749     BOOL shifted = FALSE;
750 
751     while (i < count)
752     {
753         p = input[i] & 0xff;
754         if (p == 10 || p == 13) /* NL and CR */
755         {
756             if (shifted)
757             {
758                 shifted = FALSE;
759                 j += exit_shift(output,j);
760             }
761             if (output)
762                 output[j++] = p;
763             else
764                 j++;
765         }
766         else
767         {
768             if (SJIS1(p))
769             {
770                 i++;
771                 if (i >= count)
772                     return 0;
773                 p2 = input[i] & 0xff;
774                 if (SJIS2(p2))
775                 {
776                     sjis2jis(&p,&p2);
777                     if (!shifted)
778                     {
779                         shifted = TRUE;
780                         j+=enter_shift(output,j);
781                     }
782                 }
783 
784                 if (output)
785                 {
786                     output[j++]=p;
787                     output[j++]=p2;
788                 }
789                 else
790                     j+=2;
791             }
792             else
793             {
794                 if (HANKATA(p))
795                 {
796                     if ((i+1) >= count)
797                         return 0;
798                     p2 = input[i+1] & 0xff;
799                     i+=han2zen(&p,&p2);
800                     sjis2jis(&p,&p2);
801                     if (!shifted)
802                     {
803                         shifted = TRUE;
804                         j+=enter_shift(output,j);
805                     }
806                     if (output)
807                     {
808                         output[j++]=p;
809                         output[j++]=p2;
810                     }
811                     else
812                         j+=2;
813                 }
814                 else
815                 {
816                     if (shifted)
817                     {
818                         shifted = FALSE;
819                         j += exit_shift(output,j);
820                     }
821                     if (output)
822                         output[j++]=p;
823                     else
824                         j++;
825                 }
826             }
827         }
828         i++;
829     }
830     if (shifted)
831         j += exit_shift(output,j);
832     return j;
833 }
834 
835 static UINT ConvertJISJapaneseToUnicode(LPCSTR input, DWORD count,
836                                         LPWSTR output, DWORD out_count)
837 {
838     CHAR *sjis_string;
839     UINT rc = 0;
840     sjis_string = HeapAlloc(GetProcessHeap(),0,count);
841     rc = ConvertJIS2SJIS(input,count,sjis_string);
842     if (rc)
843     {
844         TRACE("%s\n",debugstr_an(sjis_string,rc));
845         if (output)
846             rc = MultiByteToWideChar(932,0,sjis_string,rc,output,out_count);
847         else
848             rc = MultiByteToWideChar(932,0,sjis_string,rc,0,0);
849     }
850     HeapFree(GetProcessHeap(),0,sjis_string);
851     return rc;
852 
853 }
854 
855 static UINT ConvertUnknownJapaneseToUnicode(LPCSTR input, DWORD count,
856                                             LPWSTR output, DWORD out_count)
857 {
858     CHAR *sjis_string;
859     UINT rc = 0;
860     int code = DetectJapaneseCode(input,count);
861     TRACE("Japanese code %i\n",code);
862 
863     switch (code)
864     {
865     case 0:
866         if (output)
867             rc = MultiByteToWideChar(CP_ACP,0,input,count,output,out_count);
868         else
869             rc = MultiByteToWideChar(CP_ACP,0,input,count,0,0);
870         break;
871 
872     case 932:
873         if (output)
874             rc = MultiByteToWideChar(932,0,input,count,output,out_count);
875         else
876             rc = MultiByteToWideChar(932,0,input,count,0,0);
877         break;
878 
879     case 51932:
880         if (output)
881             rc = MultiByteToWideChar(20932,0,input,count,output,out_count);
882         else
883             rc = MultiByteToWideChar(20932,0,input,count,0,0);
884         break;
885 
886     case 50220:
887         sjis_string = HeapAlloc(GetProcessHeap(),0,count);
888         rc = ConvertJIS2SJIS(input,count,sjis_string);
889         if (rc)
890         {
891             TRACE("%s\n",debugstr_an(sjis_string,rc));
892             if (output)
893                 rc = MultiByteToWideChar(932,0,sjis_string,rc,output,out_count);
894             else
895                 rc = MultiByteToWideChar(932,0,sjis_string,rc,0,0);
896         }
897         HeapFree(GetProcessHeap(),0,sjis_string);
898         break;
899     }
900     return rc;
901 }
902 
903 static UINT ConvertJapaneseUnicodeToJIS(LPCWSTR input, DWORD count,
904                                         LPSTR output, DWORD out_count)
905 {
906     CHAR *sjis_string;
907     INT len;
908     UINT rc = 0;
909 
910     len = WideCharToMultiByte(932,0,input,count,0,0,NULL,NULL);
911     sjis_string = HeapAlloc(GetProcessHeap(),0,len);
912     WideCharToMultiByte(932,0,input,count,sjis_string,len,NULL,NULL);
913     TRACE("%s\n",debugstr_an(sjis_string,len));
914 
915     rc = ConvertSJIS2JIS(sjis_string, len, NULL);
916     if (out_count >= rc)
917     {
918         ConvertSJIS2JIS(sjis_string, len, output);
919     }
920     HeapFree(GetProcessHeap(),0,sjis_string);
921     return rc;
922 
923 }
924 
925 /*
926  * Dll lifetime tracking declaration
927  */
928 static void LockModule(void)
929 {
930     InterlockedIncrement(&dll_count);
931 }
932 
933 static void UnlockModule(void)
934 {
935     InterlockedDecrement(&dll_count);
936 }
937 
938 BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv)
939 {
940     switch(fdwReason) {
941         case DLL_PROCESS_ATTACH:
942             instance = hInstDLL;
943             MLANG_tls_index = TlsAlloc();
944             DisableThreadLibraryCalls(hInstDLL);
945 	    break;
946 	case DLL_PROCESS_DETACH:
947             if (lpv) break;
948             TlsFree(MLANG_tls_index);
949 	    break;
950     }
951     return TRUE;
952 }
953 
954 HRESULT WINAPI ConvertINetMultiByteToUnicode(
955     LPDWORD pdwMode,
956     DWORD dwEncoding,
957     LPCSTR pSrcStr,
958     LPINT pcSrcSize,
959     LPWSTR pDstStr,
960     LPINT pcDstSize)
961 {
962     INT src_len = -1;
963 
964     TRACE("%p %d %s %p %p %p\n", pdwMode, dwEncoding,
965           debugstr_a(pSrcStr), pcSrcSize, pDstStr, pcDstSize);
966 
967     if (!pcDstSize)
968         return E_FAIL;
969 
970     if (!pcSrcSize)
971         pcSrcSize = &src_len;
972 
973     if (!*pcSrcSize)
974     {
975         *pcDstSize = 0;
976         return S_OK;
977     }
978 
979     /* forwarding euc-jp to EUC-JP */
980     if (dwEncoding == 51932)
981         dwEncoding = 20932;
982 
983     switch (dwEncoding)
984     {
985     case CP_UNICODE:
986         if (*pcSrcSize == -1)
987             *pcSrcSize = lstrlenW((LPCWSTR)pSrcStr);
988         *pcDstSize = min(*pcSrcSize, *pcDstSize);
989         *pcSrcSize *= sizeof(WCHAR);
990         if (pDstStr)
991             memmove(pDstStr, pSrcStr, *pcDstSize * sizeof(WCHAR));
992         break;
993 
994     case 50220:
995     case 50221:
996     case 50222:
997         *pcDstSize = ConvertJISJapaneseToUnicode(pSrcStr,*pcSrcSize,pDstStr,*pcDstSize);
998         break;
999     case 50932:
1000         *pcDstSize = ConvertUnknownJapaneseToUnicode(pSrcStr,*pcSrcSize,pDstStr,*pcDstSize);
1001         break;
1002 
1003     default:
1004         if (*pcSrcSize == -1)
1005             *pcSrcSize = lstrlenA(pSrcStr);
1006 
1007         if (pDstStr)
1008             *pcDstSize = MultiByteToWideChar(dwEncoding, 0, pSrcStr, *pcSrcSize, pDstStr, *pcDstSize);
1009         else
1010             *pcDstSize = MultiByteToWideChar(dwEncoding, 0, pSrcStr, *pcSrcSize, NULL, 0);
1011         break;
1012     }
1013 
1014     if (!*pcDstSize)
1015         return E_FAIL;
1016 
1017     return S_OK;
1018 }
1019 
1020 HRESULT WINAPI ConvertINetUnicodeToMultiByte(
1021     LPDWORD pdwMode,
1022     DWORD dwEncoding,
1023     LPCWSTR pSrcStr,
1024     LPINT pcSrcSize,
1025     LPSTR pDstStr,
1026     LPINT pcDstSize)
1027 {
1028     INT destsz, size;
1029     INT src_len = -1;
1030 
1031     TRACE("%p %d %s %p %p %p\n", pdwMode, dwEncoding,
1032           debugstr_w(pSrcStr), pcSrcSize, pDstStr, pcDstSize);
1033 
1034     if (!pcDstSize)
1035         return S_OK;
1036 
1037     if (!pcSrcSize)
1038         pcSrcSize = &src_len;
1039 
1040     destsz = (pDstStr) ? *pcDstSize : 0;
1041     *pcDstSize = 0;
1042 
1043     if (!pSrcStr || !*pcSrcSize)
1044         return S_OK;
1045 
1046     if (*pcSrcSize == -1)
1047         *pcSrcSize = lstrlenW(pSrcStr);
1048 
1049     /* forwarding euc-jp to EUC-JP */
1050     if (dwEncoding == 51932)
1051         dwEncoding = 20932;
1052 
1053     if (dwEncoding == CP_UNICODE)
1054     {
1055         if (*pcSrcSize == -1)
1056             *pcSrcSize = lstrlenW(pSrcStr);
1057 
1058         size = min(*pcSrcSize, destsz) * sizeof(WCHAR);
1059         if (pDstStr)
1060             memmove(pDstStr, pSrcStr, size);
1061 
1062         if (size >= destsz)
1063             goto fail;
1064     }
1065     else if (dwEncoding == 50220 || dwEncoding == 50221 || dwEncoding == 50222)
1066     {
1067         size = ConvertJapaneseUnicodeToJIS(pSrcStr, *pcSrcSize, NULL, 0);
1068         if (!size)
1069             goto fail;
1070 
1071         if (pDstStr)
1072         {
1073             size = ConvertJapaneseUnicodeToJIS(pSrcStr, *pcSrcSize, pDstStr,
1074                                                destsz);
1075             if (!size)
1076                 goto fail;
1077         }
1078 
1079     }
1080     else
1081     {
1082         size = WideCharToMultiByte(dwEncoding, 0, pSrcStr, *pcSrcSize,
1083                                    NULL, 0, NULL, NULL);
1084         if (!size)
1085             goto fail;
1086 
1087         if (pDstStr)
1088         {
1089             size = WideCharToMultiByte(dwEncoding, 0, pSrcStr, *pcSrcSize,
1090                                        pDstStr, destsz, NULL, NULL);
1091             if (!size)
1092                 goto fail;
1093         }
1094     }
1095 
1096     *pcDstSize = size;
1097     return S_OK;
1098 
1099 fail:
1100     *pcSrcSize = 0;
1101     *pcDstSize = 0;
1102     return E_FAIL;
1103 }
1104 
1105 HRESULT WINAPI ConvertINetString(
1106     LPDWORD pdwMode,
1107     DWORD dwSrcEncoding,
1108     DWORD dwDstEncoding,
1109     LPCSTR pSrcStr,
1110     LPINT pcSrcSize,
1111     LPSTR pDstStr,
1112     LPINT pcDstSize
1113 )
1114 {
1115     TRACE("%p %d %d %s %p %p %p\n", pdwMode, dwSrcEncoding, dwDstEncoding,
1116           debugstr_a(pSrcStr), pcSrcSize, pDstStr, pcDstSize);
1117 
1118     if (dwSrcEncoding == CP_UNICODE)
1119     {
1120         INT cSrcSizeW;
1121         if (pcSrcSize && *pcSrcSize != -1)
1122         {
1123             cSrcSizeW = *pcSrcSize / sizeof(WCHAR);
1124             pcSrcSize = &cSrcSizeW;
1125         }
1126         return ConvertINetUnicodeToMultiByte(pdwMode, dwDstEncoding, (LPCWSTR)pSrcStr, pcSrcSize, pDstStr, pcDstSize);
1127     }
1128     else if (dwDstEncoding == CP_UNICODE)
1129     {
1130         HRESULT hr = ConvertINetMultiByteToUnicode(pdwMode, dwSrcEncoding, pSrcStr, pcSrcSize, (LPWSTR)pDstStr, pcDstSize);
1131         *pcDstSize *= sizeof(WCHAR);
1132         return hr;
1133     }
1134     else
1135     {
1136         INT cDstSizeW;
1137         LPWSTR pDstStrW;
1138         HRESULT hr;
1139 
1140         TRACE("convert %s from %d to %d\n", debugstr_a(pSrcStr), dwSrcEncoding, dwDstEncoding);
1141 
1142         hr = ConvertINetMultiByteToUnicode(pdwMode, dwSrcEncoding, pSrcStr, pcSrcSize, NULL, &cDstSizeW);
1143         if (hr != S_OK)
1144             return hr;
1145 
1146         pDstStrW = HeapAlloc(GetProcessHeap(), 0, cDstSizeW * sizeof(WCHAR));
1147         hr = ConvertINetMultiByteToUnicode(pdwMode, dwSrcEncoding, pSrcStr, pcSrcSize, pDstStrW, &cDstSizeW);
1148         if (hr == S_OK)
1149             hr = ConvertINetUnicodeToMultiByte(pdwMode, dwDstEncoding, pDstStrW, &cDstSizeW, pDstStr, pcDstSize);
1150 
1151         HeapFree(GetProcessHeap(), 0, pDstStrW);
1152         return hr;
1153     }
1154 }
1155 
1156 static HRESULT GetFamilyCodePage(
1157     UINT uiCodePage,
1158     UINT* puiFamilyCodePage)
1159 {
1160     UINT i, n;
1161 
1162     TRACE("%u %p\n", uiCodePage, puiFamilyCodePage);
1163 
1164     if (!puiFamilyCodePage) return S_FALSE;
1165 
1166     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
1167     {
1168         for (n = 0; n < mlang_data[i].number_of_cp; n++)
1169         {
1170             if (mlang_data[i].mime_cp_info[n].cp == uiCodePage)
1171             {
1172                 *puiFamilyCodePage = mlang_data[i].family_codepage;
1173                 return S_OK;
1174             }
1175         }
1176     }
1177 
1178     return S_FALSE;
1179 }
1180 
1181 HRESULT WINAPI IsConvertINetStringAvailable(
1182     DWORD dwSrcEncoding,
1183     DWORD dwDstEncoding)
1184 {
1185     UINT src_family, dst_family;
1186 
1187     TRACE("%d %d\n", dwSrcEncoding, dwDstEncoding);
1188 
1189     if (GetFamilyCodePage(dwSrcEncoding, &src_family) != S_OK ||
1190         GetFamilyCodePage(dwDstEncoding, &dst_family) != S_OK)
1191         return S_FALSE;
1192 
1193     if (src_family == dst_family) return S_OK;
1194 
1195     /* we can convert any codepage to/from unicode */
1196     if (src_family == CP_UNICODE || dst_family == CP_UNICODE) return S_OK;
1197 
1198     return S_FALSE;
1199 }
1200 
1201 static inline HRESULT lcid_to_rfc1766A( LCID lcid, LPSTR rfc1766, INT len )
1202 {
1203     CHAR buffer[MAX_RFC1766_NAME];
1204     INT n = GetLocaleInfoA(lcid, LOCALE_SISO639LANGNAME, buffer, MAX_RFC1766_NAME);
1205     INT i;
1206 
1207     if (n)
1208     {
1209         i = PRIMARYLANGID(lcid);
1210         if ((((i == LANG_ENGLISH) || (i == LANG_CHINESE) || (i == LANG_ARABIC)) &&
1211             (SUBLANGID(lcid) == SUBLANG_DEFAULT)) ||
1212             (SUBLANGID(lcid) > SUBLANG_DEFAULT)) {
1213 
1214             buffer[n - 1] = '-';
1215             i = GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, buffer + n, MAX_RFC1766_NAME - n);
1216             if (!i)
1217                 buffer[n - 1] = '\0';
1218         }
1219         else
1220             i = 0;
1221 
1222         LCMapStringA( LOCALE_USER_DEFAULT, LCMAP_LOWERCASE, buffer, n + i, rfc1766, len );
1223         return ((n + i) > len) ? E_INVALIDARG : S_OK;
1224     }
1225     return E_FAIL;
1226 }
1227 
1228 static inline HRESULT lcid_to_rfc1766W( LCID lcid, LPWSTR rfc1766, INT len )
1229 {
1230     WCHAR buffer[MAX_RFC1766_NAME];
1231     INT n = GetLocaleInfoW(lcid, LOCALE_SISO639LANGNAME, buffer, MAX_RFC1766_NAME);
1232     INT i;
1233 
1234     if (n)
1235     {
1236         i = PRIMARYLANGID(lcid);
1237         if ((((i == LANG_ENGLISH) || (i == LANG_CHINESE) || (i == LANG_ARABIC)) &&
1238             (SUBLANGID(lcid) == SUBLANG_DEFAULT)) ||
1239             (SUBLANGID(lcid) > SUBLANG_DEFAULT)) {
1240 
1241             buffer[n - 1] = '-';
1242             i = GetLocaleInfoW(lcid, LOCALE_SISO3166CTRYNAME, buffer + n, MAX_RFC1766_NAME - n);
1243             if (!i)
1244                 buffer[n - 1] = '\0';
1245         }
1246         else
1247             i = 0;
1248 
1249         LCMapStringW(LOCALE_USER_DEFAULT, LCMAP_LOWERCASE, buffer, n + i, rfc1766, len);
1250         return ((n + i) > len) ? E_INVALIDARG : S_OK;
1251     }
1252     return E_FAIL;
1253 }
1254 
1255 HRESULT WINAPI LcidToRfc1766A(
1256     LCID lcid,
1257     LPSTR pszRfc1766,
1258     INT nChar)
1259 {
1260     TRACE("%04x %p %u\n", lcid, pszRfc1766, nChar);
1261     if (!pszRfc1766)
1262         return E_INVALIDARG;
1263 
1264     return lcid_to_rfc1766A(lcid, pszRfc1766, nChar);
1265 }
1266 
1267 HRESULT WINAPI LcidToRfc1766W(
1268     LCID lcid,
1269     LPWSTR pszRfc1766,
1270     INT nChar)
1271 {
1272     TRACE("%04x %p %u\n", lcid, pszRfc1766, nChar);
1273     if (!pszRfc1766)
1274         return E_INVALIDARG;
1275 
1276     return lcid_to_rfc1766W(lcid, pszRfc1766, nChar);
1277 }
1278 
1279 static HRESULT lcid_from_rfc1766(IEnumRfc1766 *iface, LCID *lcid, LPCWSTR rfc1766)
1280 {
1281     RFC1766INFO info;
1282     ULONG num;
1283 
1284     while (IEnumRfc1766_Next(iface, 1, &info, &num) == S_OK)
1285     {
1286         if (!strcmpiW(info.wszRfc1766, rfc1766))
1287         {
1288             *lcid = info.lcid;
1289             return S_OK;
1290         }
1291         if (strlenW(rfc1766) == 2 && !memcmp(info.wszRfc1766, rfc1766, 2 * sizeof(WCHAR)))
1292         {
1293             *lcid = PRIMARYLANGID(info.lcid);
1294             return S_OK;
1295         }
1296     }
1297 
1298     return E_FAIL;
1299 }
1300 
1301 HRESULT WINAPI Rfc1766ToLcidW(LCID *pLocale, LPCWSTR pszRfc1766)
1302 {
1303     IEnumRfc1766 *enumrfc1766;
1304     HRESULT hr;
1305 
1306     TRACE("(%p, %s)\n", pLocale, debugstr_w(pszRfc1766));
1307 
1308     if (!pLocale || !pszRfc1766)
1309         return E_INVALIDARG;
1310 
1311     hr = EnumRfc1766_create(0, &enumrfc1766);
1312     if (FAILED(hr))
1313         return hr;
1314 
1315     hr = lcid_from_rfc1766(enumrfc1766, pLocale, pszRfc1766);
1316     IEnumRfc1766_Release(enumrfc1766);
1317 
1318     return hr;
1319 }
1320 
1321 HRESULT WINAPI Rfc1766ToLcidA(LCID *lcid, LPCSTR rfc1766A)
1322 {
1323     WCHAR rfc1766W[MAX_RFC1766_NAME + 1];
1324 
1325     if (!rfc1766A)
1326         return E_INVALIDARG;
1327 
1328     MultiByteToWideChar(CP_ACP, 0, rfc1766A, -1, rfc1766W, MAX_RFC1766_NAME);
1329     rfc1766W[MAX_RFC1766_NAME] = 0;
1330 
1331     return Rfc1766ToLcidW(lcid, rfc1766W);
1332 }
1333 
1334 static HRESULT map_font(HDC hdc, DWORD codepages, HFONT src_font, HFONT *dst_font)
1335 {
1336     struct font_list *font_list_entry;
1337     CHARSETINFO charset_info;
1338     HFONT new_font, old_font;
1339     LOGFONTW font_attr;
1340     DWORD mask, Csb[2];
1341     BOOL found_cached;
1342     UINT charset;
1343     BOOL ret;
1344     UINT i;
1345 
1346     if (hdc == NULL || src_font == NULL) return E_FAIL;
1347 
1348     for (i = 0; i < 32; i++)
1349     {
1350         mask = (DWORD)(1 << i);
1351         if (codepages & mask)
1352         {
1353             Csb[0] = mask;
1354             Csb[1] = 0x0;
1355             ret = TranslateCharsetInfo(Csb, &charset_info, TCI_SRCFONTSIG);
1356             if (!ret) continue;
1357 
1358             /* use cached font if possible */
1359             found_cached = FALSE;
1360             EnterCriticalSection(&font_cache_critical);
1361             LIST_FOR_EACH_ENTRY(font_list_entry, &font_cache, struct font_list, list_entry)
1362             {
1363                 if (font_list_entry->charset == charset_info.ciCharset &&
1364                     font_list_entry->base_font == src_font)
1365                 {
1366                     if (dst_font != NULL)
1367                         *dst_font = font_list_entry->font;
1368                     found_cached = TRUE;
1369                 }
1370             }
1371             LeaveCriticalSection(&font_cache_critical);
1372             if (found_cached) return S_OK;
1373 
1374             GetObjectW(src_font, sizeof(font_attr), &font_attr);
1375             font_attr.lfCharSet = (BYTE)charset_info.ciCharset;
1376             font_attr.lfWidth = 0;
1377             font_attr.lfFaceName[0] = 0;
1378             new_font = CreateFontIndirectW(&font_attr);
1379             if (new_font == NULL) continue;
1380 
1381             old_font = SelectObject(hdc, new_font);
1382             charset = GetTextCharset(hdc);
1383             SelectObject(hdc, old_font);
1384             if (charset == charset_info.ciCharset)
1385             {
1386                 font_list_entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*font_list_entry));
1387                 if (font_list_entry == NULL) return E_OUTOFMEMORY;
1388 
1389                 font_list_entry->base_font = src_font;
1390                 font_list_entry->font = new_font;
1391                 font_list_entry->charset = charset;
1392 
1393                 EnterCriticalSection(&font_cache_critical);
1394                 list_add_tail(&font_cache, &font_list_entry->list_entry);
1395                 LeaveCriticalSection(&font_cache_critical);
1396 
1397                 if (dst_font != NULL)
1398                     *dst_font = new_font;
1399                 return S_OK;
1400             }
1401         }
1402     }
1403 
1404     return E_FAIL;
1405 }
1406 
1407 static HRESULT release_font(HFONT font)
1408 {
1409     struct font_list *font_list_entry;
1410     HRESULT hr;
1411 
1412     hr = E_FAIL;
1413     EnterCriticalSection(&font_cache_critical);
1414     LIST_FOR_EACH_ENTRY(font_list_entry, &font_cache, struct font_list, list_entry)
1415     {
1416         if (font_list_entry->font == font)
1417         {
1418             list_remove(&font_list_entry->list_entry);
1419             DeleteObject(font);
1420             HeapFree(GetProcessHeap(), 0, font_list_entry);
1421             hr = S_OK;
1422             break;
1423         }
1424     }
1425     LeaveCriticalSection(&font_cache_critical);
1426 
1427     return hr;
1428 }
1429 
1430 static HRESULT clear_font_cache(void)
1431 {
1432     struct font_list *font_list_entry;
1433     struct font_list *font_list_entry2;
1434 
1435     EnterCriticalSection(&font_cache_critical);
1436     LIST_FOR_EACH_ENTRY_SAFE(font_list_entry, font_list_entry2, &font_cache, struct font_list, list_entry)
1437     {
1438         list_remove(&font_list_entry->list_entry);
1439         DeleteObject(font_list_entry->font);
1440         HeapFree(GetProcessHeap(), 0, font_list_entry);
1441     }
1442     LeaveCriticalSection(&font_cache_critical);
1443 
1444     return S_OK;
1445 }
1446 
1447 /******************************************************************************
1448  * MLANG ClassFactory
1449  */
1450 typedef struct {
1451     IClassFactory IClassFactory_iface;
1452     LONG ref;
1453     HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, LPVOID *ppObj);
1454 } IClassFactoryImpl;
1455 
1456 static inline IClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface)
1457 {
1458     return CONTAINING_RECORD(iface, IClassFactoryImpl, IClassFactory_iface);
1459 }
1460 
1461 struct object_creation_info
1462 {
1463     const CLSID *clsid;
1464     LPCSTR szClassName;
1465     HRESULT (*pfnCreateInstance)(IUnknown *pUnkOuter, LPVOID *ppObj);
1466 };
1467 
1468 static const struct object_creation_info object_creation[] =
1469 {
1470     { &CLSID_CMultiLanguage, "CLSID_CMultiLanguage", MultiLanguage_create },
1471     { &CLSID_CMLangConvertCharset, "CLSID_CMLangConvertCharset", MLangConvertCharset_create }
1472 };
1473 
1474 static HRESULT WINAPI MLANGCF_QueryInterface(IClassFactory *iface, REFIID riid, void **ppobj)
1475 {
1476     TRACE("%s\n", debugstr_guid(riid) );
1477 
1478     if (IsEqualGUID(riid, &IID_IUnknown)
1479 	|| IsEqualGUID(riid, &IID_IClassFactory))
1480     {
1481 	IClassFactory_AddRef(iface);
1482         *ppobj = iface;
1483 	return S_OK;
1484     }
1485 
1486     *ppobj = NULL;
1487     WARN("(%p)->(%s,%p), not found\n", iface, debugstr_guid(riid), ppobj);
1488     return E_NOINTERFACE;
1489 }
1490 
1491 static ULONG WINAPI MLANGCF_AddRef(IClassFactory *iface)
1492 {
1493     IClassFactoryImpl *This = impl_from_IClassFactory(iface);
1494     return InterlockedIncrement(&This->ref);
1495 }
1496 
1497 static ULONG WINAPI MLANGCF_Release(IClassFactory *iface)
1498 {
1499     IClassFactoryImpl *This = impl_from_IClassFactory(iface);
1500     ULONG ref = InterlockedDecrement(&This->ref);
1501 
1502     if (ref == 0)
1503     {
1504         TRACE("Destroying %p\n", This);
1505 	HeapFree(GetProcessHeap(), 0, This);
1506     }
1507 
1508     return ref;
1509 }
1510 
1511 static HRESULT WINAPI MLANGCF_CreateInstance(IClassFactory *iface, IUnknown *pOuter,
1512         REFIID riid, void **ppobj)
1513 {
1514     IClassFactoryImpl *This = impl_from_IClassFactory(iface);
1515     HRESULT hres;
1516     LPUNKNOWN punk;
1517 
1518     TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
1519 
1520     *ppobj = NULL;
1521     hres = This->pfnCreateInstance(pOuter, (LPVOID *) &punk);
1522     if (SUCCEEDED(hres)) {
1523         hres = IUnknown_QueryInterface(punk, riid, ppobj);
1524         IUnknown_Release(punk);
1525     }
1526     TRACE("returning (%p) -> %x\n", *ppobj, hres);
1527     return hres;
1528 }
1529 
1530 static HRESULT WINAPI MLANGCF_LockServer(IClassFactory *iface, BOOL dolock)
1531 {
1532     if (dolock)
1533         LockModule();
1534     else
1535         UnlockModule();
1536 
1537     return S_OK;
1538 }
1539 
1540 static const IClassFactoryVtbl MLANGCF_Vtbl =
1541 {
1542     MLANGCF_QueryInterface,
1543     MLANGCF_AddRef,
1544     MLANGCF_Release,
1545     MLANGCF_CreateInstance,
1546     MLANGCF_LockServer
1547 };
1548 
1549 /******************************************************************
1550  *		DllGetClassObject (MLANG.@)
1551  */
1552 HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID iid, LPVOID *ppv)
1553 {
1554     unsigned int i;
1555     IClassFactoryImpl *factory;
1556 
1557     TRACE("%s %s %p\n",debugstr_guid(rclsid), debugstr_guid(iid), ppv);
1558 
1559     if ( !IsEqualGUID( &IID_IClassFactory, iid )
1560 	 && ! IsEqualGUID( &IID_IUnknown, iid) )
1561 	return E_NOINTERFACE;
1562 
1563     for (i=0; i < sizeof(object_creation)/sizeof(object_creation[0]); i++)
1564     {
1565 	if (IsEqualGUID(object_creation[i].clsid, rclsid))
1566 	    break;
1567     }
1568 
1569     if (i == sizeof(object_creation)/sizeof(object_creation[0]))
1570     {
1571 	FIXME("%s: no class found.\n", debugstr_guid(rclsid));
1572 	return CLASS_E_CLASSNOTAVAILABLE;
1573     }
1574 
1575     TRACE("Creating a class factory for %s\n",object_creation[i].szClassName);
1576 
1577     factory = HeapAlloc(GetProcessHeap(), 0, sizeof(*factory));
1578     if (factory == NULL) return E_OUTOFMEMORY;
1579 
1580     factory->IClassFactory_iface.lpVtbl = &MLANGCF_Vtbl;
1581     factory->ref = 1;
1582 
1583     factory->pfnCreateInstance = object_creation[i].pfnCreateInstance;
1584 
1585     *ppv = &factory->IClassFactory_iface;
1586 
1587     TRACE("(%p) <- %p\n", ppv, &factory->IClassFactory_iface);
1588 
1589     return S_OK;
1590 }
1591 
1592 
1593 /******************************************************************************/
1594 
1595 typedef struct tagMLang_impl
1596 {
1597     IMLangFontLink IMLangFontLink_iface;
1598     IMultiLanguage IMultiLanguage_iface;
1599     IMultiLanguage3 IMultiLanguage3_iface;
1600     IMLangFontLink2 IMLangFontLink2_iface;
1601     IMLangLineBreakConsole IMLangLineBreakConsole_iface;
1602     LONG ref;
1603     DWORD total_cp, total_scripts;
1604 } MLang_impl;
1605 
1606 /******************************************************************************/
1607 
1608 typedef struct tagEnumCodePage_impl
1609 {
1610     IEnumCodePage IEnumCodePage_iface;
1611     LONG ref;
1612     MIMECPINFO *cpinfo;
1613     DWORD total, pos;
1614 } EnumCodePage_impl;
1615 
1616 static inline EnumCodePage_impl *impl_from_IEnumCodePage( IEnumCodePage *iface )
1617 {
1618     return CONTAINING_RECORD( iface, EnumCodePage_impl, IEnumCodePage_iface );
1619 }
1620 
1621 static HRESULT WINAPI fnIEnumCodePage_QueryInterface(
1622         IEnumCodePage* iface,
1623         REFIID riid,
1624         void** ppvObject)
1625 {
1626     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1627 
1628     TRACE("%p -> %s\n", This, debugstr_guid(riid) );
1629 
1630     if (IsEqualGUID(riid, &IID_IUnknown)
1631 	|| IsEqualGUID(riid, &IID_IEnumCodePage))
1632     {
1633 	IEnumCodePage_AddRef(iface);
1634         TRACE("Returning IID_IEnumCodePage %p ref = %d\n", This, This->ref);
1635 	*ppvObject = &This->IEnumCodePage_iface;
1636         return S_OK;
1637     }
1638 
1639     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
1640     return E_NOINTERFACE;
1641 }
1642 
1643 static ULONG WINAPI fnIEnumCodePage_AddRef(
1644         IEnumCodePage* iface)
1645 {
1646     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1647     return InterlockedIncrement(&This->ref);
1648 }
1649 
1650 static ULONG WINAPI fnIEnumCodePage_Release(
1651         IEnumCodePage* iface)
1652 {
1653     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1654     ULONG ref = InterlockedDecrement(&This->ref);
1655 
1656     TRACE("%p ref = %d\n", This, ref);
1657     if (ref == 0)
1658     {
1659         TRACE("Destroying %p\n", This);
1660         HeapFree(GetProcessHeap(), 0, This->cpinfo);
1661         HeapFree(GetProcessHeap(), 0, This);
1662     }
1663 
1664     return ref;
1665 }
1666 
1667 static HRESULT WINAPI fnIEnumCodePage_Clone(
1668         IEnumCodePage* iface,
1669         IEnumCodePage** ppEnum)
1670 {
1671     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1672     FIXME("%p %p\n", This, ppEnum);
1673     return E_NOTIMPL;
1674 }
1675 
1676 static  HRESULT WINAPI fnIEnumCodePage_Next(
1677         IEnumCodePage* iface,
1678         ULONG celt,
1679         PMIMECPINFO rgelt,
1680         ULONG* pceltFetched)
1681 {
1682     ULONG i;
1683     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1684 
1685     TRACE("%p %u %p %p\n", This, celt, rgelt, pceltFetched);
1686 
1687     if (!pceltFetched) return S_FALSE;
1688     *pceltFetched = 0;
1689 
1690     if (!rgelt) return S_FALSE;
1691 
1692     if (This->pos + celt > This->total)
1693         celt = This->total - This->pos;
1694 
1695     if (!celt) return S_FALSE;
1696 
1697     memcpy(rgelt, This->cpinfo + This->pos, celt * sizeof(MIMECPINFO));
1698     *pceltFetched = celt;
1699     This->pos += celt;
1700 
1701     for (i = 0; i < celt; i++)
1702     {
1703         TRACE("#%u: %08x %u %u %s %s %s %s %s %s %d\n",
1704               i, rgelt[i].dwFlags, rgelt[i].uiCodePage,
1705               rgelt[i].uiFamilyCodePage,
1706               wine_dbgstr_w(rgelt[i].wszDescription),
1707               wine_dbgstr_w(rgelt[i].wszWebCharset),
1708               wine_dbgstr_w(rgelt[i].wszHeaderCharset),
1709               wine_dbgstr_w(rgelt[i].wszBodyCharset),
1710               wine_dbgstr_w(rgelt[i].wszFixedWidthFont),
1711               wine_dbgstr_w(rgelt[i].wszProportionalFont),
1712               rgelt[i].bGDICharset);
1713     }
1714     return S_OK;
1715 }
1716 
1717 static HRESULT WINAPI fnIEnumCodePage_Reset(
1718         IEnumCodePage* iface)
1719 {
1720     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1721 
1722     TRACE("%p\n", This);
1723 
1724     This->pos = 0;
1725     return S_OK;
1726 }
1727 
1728 static  HRESULT WINAPI fnIEnumCodePage_Skip(
1729         IEnumCodePage* iface,
1730         ULONG celt)
1731 {
1732     EnumCodePage_impl *This = impl_from_IEnumCodePage( iface );
1733 
1734     TRACE("%p %u\n", This, celt);
1735 
1736     if (celt >= This->total) return S_FALSE;
1737 
1738     This->pos += celt;
1739     return S_OK;
1740 }
1741 
1742 static const IEnumCodePageVtbl IEnumCodePage_vtbl =
1743 {
1744     fnIEnumCodePage_QueryInterface,
1745     fnIEnumCodePage_AddRef,
1746     fnIEnumCodePage_Release,
1747     fnIEnumCodePage_Clone,
1748     fnIEnumCodePage_Next,
1749     fnIEnumCodePage_Reset,
1750     fnIEnumCodePage_Skip
1751 };
1752 
1753 static HRESULT EnumCodePage_create( MLang_impl* mlang, DWORD grfFlags,
1754                      LANGID LangId, IEnumCodePage** ppEnumCodePage )
1755 {
1756     EnumCodePage_impl *ecp;
1757     MIMECPINFO *cpinfo;
1758     UINT i, n;
1759 
1760     TRACE("%p, %08x, %04x, %p\n", mlang, grfFlags, LangId, ppEnumCodePage);
1761 
1762     if (!grfFlags) /* enumerate internal data base of encodings */
1763         grfFlags = MIMECONTF_MIME_LATEST;
1764 
1765     ecp = HeapAlloc( GetProcessHeap(), 0, sizeof (EnumCodePage_impl) );
1766     ecp->IEnumCodePage_iface.lpVtbl = &IEnumCodePage_vtbl;
1767     ecp->ref = 1;
1768     ecp->pos = 0;
1769     ecp->total = 0;
1770     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
1771     {
1772         for (n = 0; n < mlang_data[i].number_of_cp; n++)
1773         {
1774             if (mlang_data[i].mime_cp_info[n].flags & grfFlags)
1775                 ecp->total++;
1776         }
1777     }
1778 
1779     ecp->cpinfo = HeapAlloc(GetProcessHeap(), 0,
1780                             sizeof(MIMECPINFO) * ecp->total);
1781     cpinfo = ecp->cpinfo;
1782 
1783     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
1784     {
1785         for (n = 0; n < mlang_data[i].number_of_cp; n++)
1786         {
1787             if (mlang_data[i].mime_cp_info[n].flags & grfFlags)
1788                 fill_cp_info(&mlang_data[i], n, cpinfo++);
1789         }
1790     }
1791 
1792     TRACE("enumerated %d codepages with flags %08x\n", ecp->total, grfFlags);
1793 
1794     *ppEnumCodePage = &ecp->IEnumCodePage_iface;
1795 
1796     return S_OK;
1797 }
1798 
1799 /******************************************************************************/
1800 
1801 typedef struct tagEnumScript_impl
1802 {
1803     IEnumScript IEnumScript_iface;
1804     LONG ref;
1805     SCRIPTINFO *script_info;
1806     DWORD total, pos;
1807 } EnumScript_impl;
1808 
1809 static inline EnumScript_impl *impl_from_IEnumScript( IEnumScript *iface )
1810 {
1811     return CONTAINING_RECORD( iface, EnumScript_impl, IEnumScript_iface );
1812 }
1813 
1814 static HRESULT WINAPI fnIEnumScript_QueryInterface(
1815         IEnumScript* iface,
1816         REFIID riid,
1817         void** ppvObject)
1818 {
1819     EnumScript_impl *This = impl_from_IEnumScript( iface );
1820 
1821     TRACE("%p -> %s\n", This, debugstr_guid(riid) );
1822 
1823     if (IsEqualGUID(riid, &IID_IUnknown)
1824         || IsEqualGUID(riid, &IID_IEnumScript))
1825     {
1826         IEnumScript_AddRef(iface);
1827         TRACE("Returning IID_IEnumScript %p ref = %d\n", This, This->ref);
1828         *ppvObject = &This->IEnumScript_iface;
1829         return S_OK;
1830     }
1831 
1832     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
1833     return E_NOINTERFACE;
1834 }
1835 
1836 static ULONG WINAPI fnIEnumScript_AddRef(
1837         IEnumScript* iface)
1838 {
1839     EnumScript_impl *This = impl_from_IEnumScript( iface );
1840     return InterlockedIncrement(&This->ref);
1841 }
1842 
1843 static ULONG WINAPI fnIEnumScript_Release(
1844         IEnumScript* iface)
1845 {
1846     EnumScript_impl *This = impl_from_IEnumScript( iface );
1847     ULONG ref = InterlockedDecrement(&This->ref);
1848 
1849     TRACE("%p ref = %d\n", This, ref);
1850     if (ref == 0)
1851     {
1852         TRACE("Destroying %p\n", This);
1853         HeapFree(GetProcessHeap(), 0, This->script_info);
1854         HeapFree(GetProcessHeap(), 0, This);
1855     }
1856 
1857     return ref;
1858 }
1859 
1860 static HRESULT WINAPI fnIEnumScript_Clone(
1861         IEnumScript* iface,
1862         IEnumScript** ppEnum)
1863 {
1864     EnumScript_impl *This = impl_from_IEnumScript( iface );
1865     FIXME("%p %p: stub!\n", This, ppEnum);
1866     return E_NOTIMPL;
1867 }
1868 
1869 static  HRESULT WINAPI fnIEnumScript_Next(
1870         IEnumScript* iface,
1871         ULONG celt,
1872         PSCRIPTINFO rgelt,
1873         ULONG* pceltFetched)
1874 {
1875     EnumScript_impl *This = impl_from_IEnumScript( iface );
1876 
1877     TRACE("%p %u %p %p\n", This, celt, rgelt, pceltFetched);
1878 
1879     if (!pceltFetched || !rgelt) return E_FAIL;
1880 
1881     *pceltFetched = 0;
1882 
1883     if (This->pos + celt > This->total)
1884         celt = This->total - This->pos;
1885 
1886     if (!celt) return S_FALSE;
1887 
1888     memcpy(rgelt, This->script_info + This->pos, celt * sizeof(SCRIPTINFO));
1889     *pceltFetched = celt;
1890     This->pos += celt;
1891 
1892     return S_OK;
1893 }
1894 
1895 static HRESULT WINAPI fnIEnumScript_Reset(
1896         IEnumScript* iface)
1897 {
1898     EnumScript_impl *This = impl_from_IEnumScript( iface );
1899 
1900     TRACE("%p\n", This);
1901 
1902     This->pos = 0;
1903     return S_OK;
1904 }
1905 
1906 static  HRESULT WINAPI fnIEnumScript_Skip(
1907         IEnumScript* iface,
1908         ULONG celt)
1909 {
1910     EnumScript_impl *This = impl_from_IEnumScript( iface );
1911 
1912     TRACE("%p %u\n", This, celt);
1913 
1914     if (celt >= This->total) return S_FALSE;
1915 
1916     This->pos += celt;
1917     return S_OK;
1918 }
1919 
1920 static const IEnumScriptVtbl IEnumScript_vtbl =
1921 {
1922     fnIEnumScript_QueryInterface,
1923     fnIEnumScript_AddRef,
1924     fnIEnumScript_Release,
1925     fnIEnumScript_Clone,
1926     fnIEnumScript_Next,
1927     fnIEnumScript_Reset,
1928     fnIEnumScript_Skip
1929 };
1930 
1931 static HRESULT EnumScript_create( MLang_impl* mlang, DWORD dwFlags,
1932                      LANGID LangId, IEnumScript** ppEnumScript )
1933 {
1934     EnumScript_impl *es;
1935     UINT i;
1936 
1937     TRACE("%p, %08x, %04x, %p\n", mlang, dwFlags, LangId, ppEnumScript);
1938 
1939     if (!dwFlags) /* enumerate all available scripts */
1940         dwFlags = SCRIPTCONTF_SCRIPT_USER | SCRIPTCONTF_SCRIPT_HIDE | SCRIPTCONTF_SCRIPT_SYSTEM;
1941 
1942     es = HeapAlloc( GetProcessHeap(), 0, sizeof (EnumScript_impl) );
1943     es->IEnumScript_iface.lpVtbl = &IEnumScript_vtbl;
1944     es->ref = 1;
1945     es->pos = 0;
1946     /* do not enumerate unicode flavours */
1947     es->total = sizeof(mlang_data)/sizeof(mlang_data[0]) - 1;
1948     es->script_info = HeapAlloc(GetProcessHeap(), 0, sizeof(SCRIPTINFO) * es->total);
1949 
1950     for (i = 0; i < es->total; i++)
1951     {
1952         es->script_info[i].ScriptId = i;
1953         es->script_info[i].uiCodePage = mlang_data[i].family_codepage;
1954         MultiByteToWideChar(CP_ACP, 0, mlang_data[i].description, -1,
1955             es->script_info[i].wszDescription, MAX_SCRIPT_NAME);
1956         MultiByteToWideChar(CP_ACP, 0, mlang_data[i].fixed_font, -1,
1957             es->script_info[i].wszFixedWidthFont, MAX_MIMEFACE_NAME);
1958         MultiByteToWideChar(CP_ACP, 0, mlang_data[i].proportional_font, -1,
1959             es->script_info[i].wszProportionalFont, MAX_MIMEFACE_NAME);
1960     }
1961 
1962     TRACE("enumerated %d scripts with flags %08x\n", es->total, dwFlags);
1963 
1964     *ppEnumScript = &es->IEnumScript_iface;
1965 
1966     return S_OK;
1967 }
1968 
1969 /******************************************************************************/
1970 
1971 static inline MLang_impl *impl_from_IMLangFontLink( IMLangFontLink *iface )
1972 {
1973     return CONTAINING_RECORD( iface, MLang_impl, IMLangFontLink_iface );
1974 }
1975 
1976 static HRESULT WINAPI fnIMLangFontLink_QueryInterface(
1977         IMLangFontLink* iface,
1978         REFIID riid,
1979         void** ppvObject)
1980 {
1981     MLang_impl *This = impl_from_IMLangFontLink( iface );
1982     return IMultiLanguage3_QueryInterface( &This->IMultiLanguage3_iface, riid, ppvObject );
1983 }
1984 
1985 static ULONG WINAPI fnIMLangFontLink_AddRef(
1986         IMLangFontLink* iface)
1987 {
1988     MLang_impl *This = impl_from_IMLangFontLink( iface );
1989     return IMultiLanguage3_AddRef( &This->IMultiLanguage3_iface );
1990 }
1991 
1992 static ULONG WINAPI fnIMLangFontLink_Release(
1993         IMLangFontLink* iface)
1994 {
1995     MLang_impl *This = impl_from_IMLangFontLink( iface );
1996     return IMultiLanguage3_Release( &This->IMultiLanguage3_iface );
1997 }
1998 
1999 static HRESULT WINAPI fnIMLangFontLink_GetCharCodePages(
2000         IMLangFontLink* iface,
2001         WCHAR ch_src,
2002         DWORD* codepages)
2003 {
2004     MLang_impl *This = impl_from_IMLangFontLink( iface );
2005     return IMLangFontLink2_GetCharCodePages(&This->IMLangFontLink2_iface, ch_src, codepages);
2006 }
2007 
2008 static HRESULT WINAPI fnIMLangFontLink_GetStrCodePages(
2009         IMLangFontLink* iface,
2010         const WCHAR* src,
2011         LONG src_len,
2012         DWORD priority_cp,
2013         DWORD* codepages,
2014         LONG* ret_len)
2015 {
2016     MLang_impl *This = impl_from_IMLangFontLink( iface );
2017     return IMLangFontLink2_GetStrCodePages(&This->IMLangFontLink2_iface, src, src_len, priority_cp,
2018         codepages, ret_len);
2019 }
2020 
2021 static HRESULT WINAPI fnIMLangFontLink_CodePageToCodePages(
2022         IMLangFontLink* iface,
2023         UINT codepage,
2024         DWORD* codepages)
2025 {
2026     MLang_impl *This = impl_from_IMLangFontLink( iface );
2027     return IMLangFontLink2_CodePageToCodePages(&This->IMLangFontLink2_iface, codepage, codepages);
2028 }
2029 
2030 static HRESULT WINAPI fnIMLangFontLink_CodePagesToCodePage(
2031         IMLangFontLink* iface,
2032         DWORD codepages,
2033         UINT def_codepage,
2034         UINT* codepage)
2035 {
2036     MLang_impl *This = impl_from_IMLangFontLink(iface);
2037     return IMLangFontLink2_CodePagesToCodePage(&This->IMLangFontLink2_iface, codepages,
2038         def_codepage, codepage);
2039 }
2040 
2041 static HRESULT WINAPI fnIMLangFontLink_GetFontCodePages(
2042         IMLangFontLink* iface,
2043         HDC hdc,
2044         HFONT hfont,
2045         DWORD* codepages)
2046 {
2047     MLang_impl *This = impl_from_IMLangFontLink(iface);
2048     return IMLangFontLink2_GetFontCodePages(&This->IMLangFontLink2_iface, hdc, hfont, codepages);
2049 }
2050 
2051 static HRESULT WINAPI fnIMLangFontLink_MapFont(
2052         IMLangFontLink* iface,
2053         HDC hDC,
2054         DWORD dwCodePages,
2055         HFONT hSrcFont,
2056         HFONT* phDestFont)
2057 {
2058     TRACE("(%p)->%p %08x %p %p\n",iface, hDC, dwCodePages, hSrcFont, phDestFont);
2059 
2060     return map_font(hDC, dwCodePages, hSrcFont, phDestFont);
2061 }
2062 
2063 static HRESULT WINAPI fnIMLangFontLink_ReleaseFont(
2064         IMLangFontLink* iface,
2065         HFONT hFont)
2066 {
2067     TRACE("(%p)->%p\n",iface, hFont);
2068 
2069     return release_font(hFont);
2070 }
2071 
2072 static HRESULT WINAPI fnIMLangFontLink_ResetFontMapping(
2073         IMLangFontLink* iface)
2074 {
2075     TRACE("(%p)\n",iface);
2076 
2077     return clear_font_cache();
2078 }
2079 
2080 
2081 static const IMLangFontLinkVtbl IMLangFontLink_vtbl =
2082 {
2083     fnIMLangFontLink_QueryInterface,
2084     fnIMLangFontLink_AddRef,
2085     fnIMLangFontLink_Release,
2086     fnIMLangFontLink_GetCharCodePages,
2087     fnIMLangFontLink_GetStrCodePages,
2088     fnIMLangFontLink_CodePageToCodePages,
2089     fnIMLangFontLink_CodePagesToCodePage,
2090     fnIMLangFontLink_GetFontCodePages,
2091     fnIMLangFontLink_MapFont,
2092     fnIMLangFontLink_ReleaseFont,
2093     fnIMLangFontLink_ResetFontMapping,
2094 };
2095 
2096 /******************************************************************************/
2097 
2098 static inline MLang_impl *impl_from_IMultiLanguage( IMultiLanguage *iface )
2099 {
2100     return CONTAINING_RECORD( iface, MLang_impl, IMultiLanguage_iface );
2101 }
2102 
2103 static HRESULT WINAPI fnIMultiLanguage_QueryInterface(
2104     IMultiLanguage* iface,
2105     REFIID riid,
2106     void** obj)
2107 {
2108     MLang_impl *This = impl_from_IMultiLanguage( iface );
2109     return IMultiLanguage3_QueryInterface(&This->IMultiLanguage3_iface, riid, obj);
2110 }
2111 
2112 static ULONG WINAPI fnIMultiLanguage_AddRef( IMultiLanguage* iface )
2113 {
2114     MLang_impl *This = impl_from_IMultiLanguage( iface );
2115     return IMultiLanguage3_AddRef(&This->IMultiLanguage3_iface);
2116 }
2117 
2118 static ULONG WINAPI fnIMultiLanguage_Release( IMultiLanguage* iface )
2119 {
2120     MLang_impl *This = impl_from_IMultiLanguage( iface );
2121     return IMultiLanguage3_Release(&This->IMultiLanguage3_iface);
2122 }
2123 
2124 static HRESULT WINAPI fnIMultiLanguage_GetNumberOfCodePageInfo(
2125     IMultiLanguage* iface,
2126     UINT* cp)
2127 {
2128     MLang_impl *This = impl_from_IMultiLanguage( iface );
2129     TRACE("(%p, %p)\n", This, cp);
2130     return IMultiLanguage3_GetNumberOfCodePageInfo(&This->IMultiLanguage3_iface, cp);
2131 }
2132 
2133 static HRESULT WINAPI fnIMultiLanguage_GetCodePageInfo(
2134     IMultiLanguage* iface,
2135     UINT uiCodePage,
2136     PMIMECPINFO pCodePageInfo)
2137 {
2138     UINT i, n;
2139     MLang_impl *This = impl_from_IMultiLanguage( iface );
2140 
2141     TRACE("%p, %u, %p\n", This, uiCodePage, pCodePageInfo);
2142 
2143     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
2144     {
2145         for (n = 0; n < mlang_data[i].number_of_cp; n++)
2146         {
2147             if (mlang_data[i].mime_cp_info[n].cp == uiCodePage)
2148             {
2149                 fill_cp_info(&mlang_data[i], n, pCodePageInfo);
2150                 return S_OK;
2151             }
2152         }
2153     }
2154 
2155     return S_FALSE;
2156 }
2157 
2158 static HRESULT WINAPI fnIMultiLanguage_GetFamilyCodePage(
2159     IMultiLanguage* iface,
2160     UINT cp,
2161     UINT* family_cp)
2162 {
2163     MLang_impl *This = impl_from_IMultiLanguage( iface );
2164     return IMultiLanguage3_GetFamilyCodePage(&This->IMultiLanguage3_iface, cp, family_cp);
2165 }
2166 
2167 static HRESULT WINAPI fnIMultiLanguage_EnumCodePages(
2168     IMultiLanguage* iface,
2169     DWORD grfFlags,
2170     IEnumCodePage** ppEnumCodePage)
2171 {
2172     MLang_impl *This = impl_from_IMultiLanguage( iface );
2173 
2174     TRACE("%p %08x %p\n", This, grfFlags, ppEnumCodePage);
2175 
2176     return EnumCodePage_create( This, grfFlags, 0, ppEnumCodePage );
2177 }
2178 
2179 static HRESULT WINAPI fnIMultiLanguage_GetCharsetInfo(
2180     IMultiLanguage* iface,
2181     BSTR Charset,
2182     PMIMECSETINFO pCharsetInfo)
2183 {
2184     MLang_impl *This = impl_from_IMultiLanguage( iface );
2185     return IMultiLanguage3_GetCharsetInfo( &This->IMultiLanguage3_iface, Charset, pCharsetInfo );
2186 }
2187 
2188 static HRESULT WINAPI fnIMultiLanguage_IsConvertible(
2189     IMultiLanguage* iface,
2190     DWORD src_enc,
2191     DWORD dst_enc)
2192 {
2193     MLang_impl *This = impl_from_IMultiLanguage( iface );
2194     return IMultiLanguage3_IsConvertible(&This->IMultiLanguage3_iface, src_enc, dst_enc);
2195 }
2196 
2197 static HRESULT WINAPI fnIMultiLanguage_ConvertString(
2198     IMultiLanguage* iface,
2199     DWORD* mode,
2200     DWORD src_enc,
2201     DWORD dst_enc,
2202     BYTE* src,
2203     UINT* src_size,
2204     BYTE* dest,
2205     UINT* dest_size)
2206 {
2207     MLang_impl *This = impl_from_IMultiLanguage( iface );
2208     return IMultiLanguage3_ConvertString(&This->IMultiLanguage3_iface, mode, src_enc,
2209         dst_enc, src, src_size, dest, dest_size);
2210 }
2211 
2212 static HRESULT WINAPI fnIMultiLanguage_ConvertStringToUnicode(
2213     IMultiLanguage* iface,
2214     DWORD* mode,
2215     DWORD src_enc,
2216     CHAR* src,
2217     UINT* src_size,
2218     WCHAR* dest,
2219     UINT* dest_size)
2220 {
2221     MLang_impl *This = impl_from_IMultiLanguage( iface );
2222     return IMultiLanguage3_ConvertStringToUnicode(&This->IMultiLanguage3_iface,
2223         mode, src_enc, src, src_size, dest, dest_size);
2224 }
2225 
2226 static HRESULT WINAPI fnIMultiLanguage_ConvertStringFromUnicode(
2227     IMultiLanguage* iface,
2228     DWORD* mode,
2229     DWORD encoding,
2230     WCHAR* src,
2231     UINT* src_size,
2232     CHAR* dest,
2233     UINT* dest_size)
2234 {
2235     MLang_impl *This = impl_from_IMultiLanguage(iface);
2236     return IMultiLanguage3_ConvertStringFromUnicode(&This->IMultiLanguage3_iface,
2237         mode, encoding, src, src_size, dest, dest_size);
2238 }
2239 
2240 static HRESULT WINAPI fnIMultiLanguage_ConvertStringReset(
2241     IMultiLanguage* iface)
2242 {
2243     MLang_impl *This = impl_from_IMultiLanguage( iface );
2244     return IMultiLanguage3_ConvertStringReset(&This->IMultiLanguage3_iface);
2245 }
2246 
2247 static HRESULT WINAPI fnIMultiLanguage_GetRfc1766FromLcid(
2248     IMultiLanguage* iface,
2249     LCID lcid,
2250     BSTR* pbstrRfc1766)
2251 {
2252     MLang_impl *This = impl_from_IMultiLanguage(iface);
2253     return IMultiLanguage3_GetRfc1766FromLcid(&This->IMultiLanguage3_iface, lcid, pbstrRfc1766);
2254 }
2255 
2256 static HRESULT WINAPI fnIMultiLanguage_GetLcidFromRfc1766(
2257     IMultiLanguage* iface,
2258     LCID* locale,
2259     BSTR rfc1766)
2260 {
2261     MLang_impl *This = impl_from_IMultiLanguage(iface);
2262     return IMultiLanguage3_GetLcidFromRfc1766(&This->IMultiLanguage3_iface, locale, rfc1766);
2263 }
2264 
2265 /******************************************************************************/
2266 
2267 typedef struct tagEnumRfc1766_impl
2268 {
2269     IEnumRfc1766 IEnumRfc1766_iface;
2270     LONG ref;
2271     RFC1766INFO *info;
2272     DWORD total, pos;
2273 } EnumRfc1766_impl;
2274 
2275 static inline EnumRfc1766_impl *impl_from_IEnumRfc1766( IEnumRfc1766 *iface )
2276 {
2277     return CONTAINING_RECORD( iface, EnumRfc1766_impl, IEnumRfc1766_iface );
2278 }
2279 
2280 static HRESULT WINAPI fnIEnumRfc1766_QueryInterface(
2281         IEnumRfc1766 *iface,
2282         REFIID riid,
2283         void** ppvObject)
2284 {
2285     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2286 
2287     TRACE("%p -> %s\n", This, debugstr_guid(riid) );
2288 
2289     if (IsEqualGUID(riid, &IID_IUnknown)
2290         || IsEqualGUID(riid, &IID_IEnumRfc1766))
2291     {
2292         IEnumRfc1766_AddRef(iface);
2293         TRACE("Returning IID_IEnumRfc1766 %p ref = %d\n", This, This->ref);
2294         *ppvObject = &This->IEnumRfc1766_iface;
2295         return S_OK;
2296     }
2297 
2298     WARN("(%p) -> (%s,%p), not found\n",This,debugstr_guid(riid),ppvObject);
2299     return E_NOINTERFACE;
2300 }
2301 
2302 static ULONG WINAPI fnIEnumRfc1766_AddRef(
2303         IEnumRfc1766 *iface)
2304 {
2305     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2306     return InterlockedIncrement(&This->ref);
2307 }
2308 
2309 static ULONG WINAPI fnIEnumRfc1766_Release(
2310         IEnumRfc1766 *iface)
2311 {
2312     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2313     ULONG ref = InterlockedDecrement(&This->ref);
2314 
2315     TRACE("%p ref = %d\n", This, ref);
2316     if (ref == 0)
2317     {
2318         TRACE("Destroying %p\n", This);
2319         HeapFree(GetProcessHeap(), 0, This->info);
2320         HeapFree(GetProcessHeap(), 0, This);
2321     }
2322     return ref;
2323 }
2324 
2325 static HRESULT WINAPI fnIEnumRfc1766_Clone(
2326         IEnumRfc1766 *iface,
2327         IEnumRfc1766 **ppEnum)
2328 {
2329     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2330 
2331     FIXME("%p %p\n", This, ppEnum);
2332     return E_NOTIMPL;
2333 }
2334 
2335 static  HRESULT WINAPI fnIEnumRfc1766_Next(
2336         IEnumRfc1766 *iface,
2337         ULONG celt,
2338         PRFC1766INFO rgelt,
2339         ULONG *pceltFetched)
2340 {
2341     ULONG i;
2342     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2343 
2344     TRACE("%p %u %p %p\n", This, celt, rgelt, pceltFetched);
2345 
2346     if (!pceltFetched) return S_FALSE;
2347     *pceltFetched = 0;
2348 
2349     if (!rgelt) return S_FALSE;
2350 
2351     if (This->pos + celt > This->total)
2352         celt = This->total - This->pos;
2353 
2354     if (!celt) return S_FALSE;
2355 
2356     memcpy(rgelt, This->info + This->pos, celt * sizeof(RFC1766INFO));
2357     *pceltFetched = celt;
2358     This->pos += celt;
2359 
2360     for (i = 0; i < celt; i++)
2361     {
2362         TRACE("#%u: %08x %s %s\n",
2363               i, rgelt[i].lcid,
2364               wine_dbgstr_w(rgelt[i].wszRfc1766),
2365               wine_dbgstr_w(rgelt[i].wszLocaleName));
2366     }
2367     return S_OK;
2368 }
2369 
2370 static HRESULT WINAPI fnIEnumRfc1766_Reset(
2371         IEnumRfc1766 *iface)
2372 {
2373     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2374 
2375     TRACE("%p\n", This);
2376 
2377     This->pos = 0;
2378     return S_OK;
2379 }
2380 
2381 static  HRESULT WINAPI fnIEnumRfc1766_Skip(
2382         IEnumRfc1766 *iface,
2383         ULONG celt)
2384 {
2385     EnumRfc1766_impl *This = impl_from_IEnumRfc1766( iface );
2386 
2387     TRACE("%p %u\n", This, celt);
2388 
2389     if (celt >= This->total) return S_FALSE;
2390 
2391     This->pos += celt;
2392     return S_OK;
2393 }
2394 
2395 static const IEnumRfc1766Vtbl IEnumRfc1766_vtbl =
2396 {
2397     fnIEnumRfc1766_QueryInterface,
2398     fnIEnumRfc1766_AddRef,
2399     fnIEnumRfc1766_Release,
2400     fnIEnumRfc1766_Clone,
2401     fnIEnumRfc1766_Next,
2402     fnIEnumRfc1766_Reset,
2403     fnIEnumRfc1766_Skip
2404 };
2405 
2406 struct enum_locales_data
2407 {
2408     RFC1766INFO *info;
2409     DWORD total, allocated;
2410 };
2411 
2412 static BOOL CALLBACK enum_locales_proc(LPWSTR locale)
2413 {
2414     WCHAR *end;
2415     struct enum_locales_data *data = TlsGetValue(MLANG_tls_index);
2416     RFC1766INFO *info;
2417 
2418     TRACE("%s\n", debugstr_w(locale));
2419 
2420     if (data->total >= data->allocated)
2421     {
2422         data->allocated += 32;
2423         data->info = HeapReAlloc(GetProcessHeap(), 0, data->info, data->allocated * sizeof(RFC1766INFO));
2424         if (!data->info) return FALSE;
2425     }
2426 
2427     info = &data->info[data->total];
2428 
2429     info->lcid = strtolW(locale, &end, 16);
2430     if (*end) /* invalid number */
2431         return FALSE;
2432 
2433     info->wszRfc1766[0] = 0;
2434     lcid_to_rfc1766W( info->lcid, info->wszRfc1766, MAX_RFC1766_NAME );
2435 
2436     info->wszLocaleName[0] = 0;
2437     GetLocaleInfoW(info->lcid, LOCALE_SLANGUAGE, info->wszLocaleName, MAX_LOCALE_NAME);
2438     TRACE("ISO639: %s SLANGUAGE: %s\n", wine_dbgstr_w(info->wszRfc1766), wine_dbgstr_w(info->wszLocaleName));
2439 
2440     data->total++;
2441 
2442     return TRUE;
2443 }
2444 
2445 static HRESULT EnumRfc1766_create(LANGID LangId, IEnumRfc1766 **ppEnum)
2446 {
2447     EnumRfc1766_impl *rfc;
2448     struct enum_locales_data data;
2449 
2450     TRACE("%04x, %p\n", LangId, ppEnum);
2451 
2452     rfc = HeapAlloc( GetProcessHeap(), 0, sizeof(EnumRfc1766_impl) );
2453     rfc->IEnumRfc1766_iface.lpVtbl = &IEnumRfc1766_vtbl;
2454     rfc->ref = 1;
2455     rfc->pos = 0;
2456     rfc->total = 0;
2457 
2458     data.total = 0;
2459     data.allocated = 160;
2460     data.info = HeapAlloc(GetProcessHeap(), 0, data.allocated * sizeof(RFC1766INFO));
2461     if (!data.info)
2462     {
2463         HeapFree(GetProcessHeap(), 0, rfc);
2464         return E_OUTOFMEMORY;
2465     }
2466 
2467     TlsSetValue(MLANG_tls_index, &data);
2468     EnumSystemLocalesW(enum_locales_proc, 0/*LOCALE_SUPPORTED*/);
2469     TlsSetValue(MLANG_tls_index, NULL);
2470 
2471     TRACE("enumerated %d rfc1766 structures\n", data.total);
2472 
2473     if (!data.total)
2474     {
2475         HeapFree(GetProcessHeap(), 0, data.info);
2476         HeapFree(GetProcessHeap(), 0, rfc);
2477         return E_FAIL;
2478     }
2479 
2480     rfc->info = data.info;
2481     rfc->total = data.total;
2482 
2483     *ppEnum = &rfc->IEnumRfc1766_iface;
2484     return S_OK;
2485 }
2486 
2487 static HRESULT WINAPI fnIMultiLanguage_EnumRfc1766(
2488     IMultiLanguage *iface,
2489     IEnumRfc1766 **ppEnumRfc1766)
2490 {
2491     MLang_impl *This = impl_from_IMultiLanguage( iface );
2492 
2493     TRACE("%p %p\n", This, ppEnumRfc1766);
2494 
2495     return EnumRfc1766_create(0, ppEnumRfc1766);
2496 }
2497 
2498 /******************************************************************************/
2499 
2500 static HRESULT WINAPI fnIMultiLanguage_GetRfc1766Info(
2501     IMultiLanguage* iface,
2502     LCID Locale,
2503     PRFC1766INFO pRfc1766Info)
2504 {
2505     LCTYPE type = LOCALE_SLANGUAGE;
2506 
2507     TRACE("(%p, 0x%04x, %p)\n", iface, Locale, pRfc1766Info);
2508 
2509     if (!pRfc1766Info)
2510         return E_INVALIDARG;
2511 
2512     if ((PRIMARYLANGID(Locale) == LANG_ENGLISH) ||
2513         (PRIMARYLANGID(Locale) == LANG_CHINESE) ||
2514         (PRIMARYLANGID(Locale) == LANG_ARABIC)) {
2515 
2516         if (!SUBLANGID(Locale))
2517             type = LOCALE_SENGLANGUAGE; /* suppress country */
2518     }
2519     else
2520     {
2521         if (!SUBLANGID(Locale)) {
2522             TRACE("SUBLANGID missing in 0x%04x\n", Locale);
2523             return E_FAIL;
2524         }
2525     }
2526 
2527     pRfc1766Info->lcid = Locale;
2528     pRfc1766Info->wszRfc1766[0] = 0;
2529     pRfc1766Info->wszLocaleName[0] = 0;
2530 
2531     if ((!lcid_to_rfc1766W(Locale, pRfc1766Info->wszRfc1766, MAX_RFC1766_NAME)) &&
2532         (GetLocaleInfoW(Locale, type, pRfc1766Info->wszLocaleName, MAX_LOCALE_NAME) > 0))
2533             return S_OK;
2534 
2535     /* Locale not supported */
2536     return E_INVALIDARG;
2537 }
2538 
2539 static HRESULT WINAPI fnIMultiLanguage_CreateConvertCharset(
2540     IMultiLanguage* iface,
2541     UINT src_cp,
2542     UINT dst_cp,
2543     DWORD prop,
2544     IMLangConvertCharset** convert_charset)
2545 {
2546     MLang_impl *This = impl_from_IMultiLanguage(iface);
2547     return IMultiLanguage3_CreateConvertCharset(&This->IMultiLanguage3_iface, src_cp, dst_cp, prop, convert_charset);
2548 }
2549 
2550 static const IMultiLanguageVtbl IMultiLanguage_vtbl =
2551 {
2552     fnIMultiLanguage_QueryInterface,
2553     fnIMultiLanguage_AddRef,
2554     fnIMultiLanguage_Release,
2555     fnIMultiLanguage_GetNumberOfCodePageInfo,
2556     fnIMultiLanguage_GetCodePageInfo,
2557     fnIMultiLanguage_GetFamilyCodePage,
2558     fnIMultiLanguage_EnumCodePages,
2559     fnIMultiLanguage_GetCharsetInfo,
2560     fnIMultiLanguage_IsConvertible,
2561     fnIMultiLanguage_ConvertString,
2562     fnIMultiLanguage_ConvertStringToUnicode,
2563     fnIMultiLanguage_ConvertStringFromUnicode,
2564     fnIMultiLanguage_ConvertStringReset,
2565     fnIMultiLanguage_GetRfc1766FromLcid,
2566     fnIMultiLanguage_GetLcidFromRfc1766,
2567     fnIMultiLanguage_EnumRfc1766,
2568     fnIMultiLanguage_GetRfc1766Info,
2569     fnIMultiLanguage_CreateConvertCharset,
2570 };
2571 
2572 
2573 /******************************************************************************/
2574 
2575 static inline MLang_impl *impl_from_IMultiLanguage3( IMultiLanguage3 *iface )
2576 {
2577     return CONTAINING_RECORD( iface, MLang_impl, IMultiLanguage3_iface );
2578 }
2579 
2580 static HRESULT WINAPI fnIMultiLanguage3_QueryInterface(
2581     IMultiLanguage3* iface,
2582     REFIID riid,
2583     void** obj)
2584 {
2585     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2586 
2587     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
2588 
2589     if (IsEqualGUID(riid, &IID_IUnknown) ||
2590         IsEqualGUID(riid, &IID_IMultiLanguage))
2591     {
2592         *obj = &This->IMultiLanguage_iface;
2593     }
2594     else if (IsEqualGUID(riid, &IID_IMLangCodePages) ||
2595              IsEqualGUID(riid, &IID_IMLangFontLink))
2596     {
2597         *obj = &This->IMLangFontLink_iface;
2598     }
2599     else if (IsEqualGUID(riid, &IID_IMLangFontLink2))
2600     {
2601         *obj = &This->IMLangFontLink2_iface;
2602     }
2603     else if (IsEqualGUID(riid, &IID_IMultiLanguage2) ||
2604              IsEqualGUID(riid, &IID_IMultiLanguage3))
2605     {
2606         *obj = &This->IMultiLanguage3_iface;
2607     }
2608     else if (IsEqualGUID(riid, &IID_IMLangLineBreakConsole))
2609     {
2610         *obj = &This->IMLangLineBreakConsole_iface;
2611     }
2612     else
2613     {
2614         WARN("(%p)->(%s,%p),not found\n", This, debugstr_guid(riid), obj);
2615         *obj = NULL;
2616         return E_NOINTERFACE;
2617     }
2618 
2619     IMultiLanguage3_AddRef(iface);
2620     return S_OK;
2621 }
2622 
2623 static ULONG WINAPI fnIMultiLanguage3_AddRef( IMultiLanguage3* iface )
2624 {
2625     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2626     return InterlockedIncrement(&This->ref);
2627 }
2628 
2629 static ULONG WINAPI fnIMultiLanguage3_Release( IMultiLanguage3* iface )
2630 {
2631     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2632     ULONG ref = InterlockedDecrement(&This->ref);
2633 
2634     TRACE("(%p)->(%d)\n", This, ref);
2635     if (ref == 0)
2636     {
2637         HeapFree(GetProcessHeap(), 0, This);
2638         UnlockModule();
2639     }
2640 
2641     return ref;
2642 }
2643 
2644 static HRESULT WINAPI fnIMultiLanguage3_GetNumberOfCodePageInfo(
2645     IMultiLanguage3* iface,
2646     UINT* pcCodePage)
2647 {
2648     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2649 
2650     TRACE("%p, %p\n", This, pcCodePage);
2651 
2652     if (!pcCodePage) return E_INVALIDARG;
2653 
2654     *pcCodePage = This->total_cp;
2655     return S_OK;
2656 }
2657 
2658 static void fill_cp_info(const struct mlang_data *ml_data, UINT index, MIMECPINFO *mime_cp_info)
2659 {
2660     CHARSETINFO csi;
2661 
2662     if (TranslateCharsetInfo((DWORD*)(DWORD_PTR)ml_data->family_codepage, &csi,
2663                              TCI_SRCCODEPAGE))
2664         mime_cp_info->bGDICharset = csi.ciCharset;
2665     else
2666         mime_cp_info->bGDICharset = DEFAULT_CHARSET;
2667 
2668     mime_cp_info->dwFlags = ml_data->mime_cp_info[index].flags;
2669     mime_cp_info->uiCodePage = ml_data->mime_cp_info[index].cp;
2670     mime_cp_info->uiFamilyCodePage = ml_data->family_codepage;
2671     MultiByteToWideChar(CP_ACP, 0, ml_data->mime_cp_info[index].description, -1,
2672                         mime_cp_info->wszDescription, sizeof(mime_cp_info->wszDescription)/sizeof(WCHAR));
2673     MultiByteToWideChar(CP_ACP, 0, ml_data->mime_cp_info[index].web_charset, -1,
2674                         mime_cp_info->wszWebCharset, sizeof(mime_cp_info->wszWebCharset)/sizeof(WCHAR));
2675     MultiByteToWideChar(CP_ACP, 0, ml_data->mime_cp_info[index].header_charset, -1,
2676                         mime_cp_info->wszHeaderCharset, sizeof(mime_cp_info->wszHeaderCharset)/sizeof(WCHAR));
2677     MultiByteToWideChar(CP_ACP, 0, ml_data->mime_cp_info[index].body_charset, -1,
2678                         mime_cp_info->wszBodyCharset, sizeof(mime_cp_info->wszBodyCharset)/sizeof(WCHAR));
2679 
2680     MultiByteToWideChar(CP_ACP, 0, ml_data->fixed_font, -1,
2681         mime_cp_info->wszFixedWidthFont, sizeof(mime_cp_info->wszFixedWidthFont)/sizeof(WCHAR));
2682     MultiByteToWideChar(CP_ACP, 0, ml_data->proportional_font, -1,
2683         mime_cp_info->wszProportionalFont, sizeof(mime_cp_info->wszProportionalFont)/sizeof(WCHAR));
2684 
2685     TRACE("%08x %u %u %s %s %s %s %s %s %d\n",
2686           mime_cp_info->dwFlags, mime_cp_info->uiCodePage,
2687           mime_cp_info->uiFamilyCodePage,
2688           wine_dbgstr_w(mime_cp_info->wszDescription),
2689           wine_dbgstr_w(mime_cp_info->wszWebCharset),
2690           wine_dbgstr_w(mime_cp_info->wszHeaderCharset),
2691           wine_dbgstr_w(mime_cp_info->wszBodyCharset),
2692           wine_dbgstr_w(mime_cp_info->wszFixedWidthFont),
2693           wine_dbgstr_w(mime_cp_info->wszProportionalFont),
2694           mime_cp_info->bGDICharset);
2695 }
2696 
2697 static HRESULT WINAPI fnIMultiLanguage3_GetCodePageInfo(
2698     IMultiLanguage3* iface,
2699     UINT uiCodePage,
2700     LANGID LangId,
2701     PMIMECPINFO pCodePageInfo)
2702 {
2703     UINT i, n;
2704     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2705 
2706     TRACE("%p, %u, %04x, %p\n", This, uiCodePage, LangId, pCodePageInfo);
2707 
2708     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
2709     {
2710         for (n = 0; n < mlang_data[i].number_of_cp; n++)
2711         {
2712             if (mlang_data[i].mime_cp_info[n].cp == uiCodePage)
2713             {
2714                 fill_cp_info(&mlang_data[i], n, pCodePageInfo);
2715                 return S_OK;
2716             }
2717         }
2718     }
2719 
2720     return S_FALSE;
2721 }
2722 
2723 static HRESULT WINAPI fnIMultiLanguage3_GetFamilyCodePage(
2724     IMultiLanguage3* iface,
2725     UINT uiCodePage,
2726     UINT* puiFamilyCodePage)
2727 {
2728     return GetFamilyCodePage(uiCodePage, puiFamilyCodePage);
2729 }
2730 
2731 static HRESULT WINAPI fnIMultiLanguage3_EnumCodePages(
2732     IMultiLanguage3* iface,
2733     DWORD grfFlags,
2734     LANGID LangId,
2735     IEnumCodePage** ppEnumCodePage)
2736 {
2737     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2738 
2739     TRACE("%p %08x %04x %p\n", This, grfFlags, LangId, ppEnumCodePage);
2740 
2741     return EnumCodePage_create( This, grfFlags, LangId, ppEnumCodePage );
2742 }
2743 
2744 static HRESULT WINAPI fnIMultiLanguage3_GetCharsetInfo(
2745     IMultiLanguage3* iface,
2746     BSTR Charset,
2747     PMIMECSETINFO pCharsetInfo)
2748 {
2749     UINT i, n;
2750     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2751 
2752     TRACE("%p %s %p\n", This, debugstr_w(Charset), pCharsetInfo);
2753 
2754     if (!pCharsetInfo) return E_FAIL;
2755 
2756     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
2757     {
2758         for (n = 0; n < mlang_data[i].number_of_cp; n++)
2759         {
2760             WCHAR csetW[MAX_MIMECSET_NAME];
2761 
2762             MultiByteToWideChar(CP_ACP, 0, mlang_data[i].mime_cp_info[n].web_charset, -1, csetW, MAX_MIMECSET_NAME);
2763             if (!lstrcmpiW(Charset, csetW))
2764             {
2765                 pCharsetInfo->uiCodePage = mlang_data[i].family_codepage;
2766                 pCharsetInfo->uiInternetEncoding = mlang_data[i].mime_cp_info[n].cp;
2767                 strcpyW(pCharsetInfo->wszCharset, csetW);
2768                 return S_OK;
2769             }
2770             if (mlang_data[i].mime_cp_info[n].alias && !lstrcmpiW(Charset, mlang_data[i].mime_cp_info[n].alias))
2771             {
2772                 pCharsetInfo->uiCodePage = mlang_data[i].family_codepage;
2773                 pCharsetInfo->uiInternetEncoding = mlang_data[i].mime_cp_info[n].cp;
2774                 strcpyW(pCharsetInfo->wszCharset, mlang_data[i].mime_cp_info[n].alias);
2775                 return S_OK;
2776             }
2777         }
2778     }
2779 
2780     /* FIXME:
2781      * Since we do not support charsets like iso-2022-jp and do not have
2782      * them in our database as a primary (web_charset) encoding this loop
2783      * does an attempt to 'approximate' charset name by header_charset.
2784      */
2785     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
2786     {
2787         for (n = 0; n < mlang_data[i].number_of_cp; n++)
2788         {
2789             WCHAR csetW[MAX_MIMECSET_NAME];
2790 
2791             MultiByteToWideChar(CP_ACP, 0, mlang_data[i].mime_cp_info[n].header_charset, -1, csetW, MAX_MIMECSET_NAME);
2792             if (!lstrcmpiW(Charset, csetW))
2793             {
2794                 pCharsetInfo->uiCodePage = mlang_data[i].family_codepage;
2795                 pCharsetInfo->uiInternetEncoding = mlang_data[i].mime_cp_info[n].cp;
2796                 strcpyW(pCharsetInfo->wszCharset, csetW);
2797                 return S_OK;
2798             }
2799         }
2800     }
2801 
2802     return E_FAIL;
2803 }
2804 
2805 static HRESULT WINAPI fnIMultiLanguage3_IsConvertible(
2806     IMultiLanguage3* iface,
2807     DWORD dwSrcEncoding,
2808     DWORD dwDstEncoding)
2809 {
2810     return IsConvertINetStringAvailable(dwSrcEncoding, dwDstEncoding);
2811 }
2812 
2813 static HRESULT WINAPI fnIMultiLanguage3_ConvertString(
2814     IMultiLanguage3* iface,
2815     DWORD* pdwMode,
2816     DWORD dwSrcEncoding,
2817     DWORD dwDstEncoding,
2818     BYTE* pSrcStr,
2819     UINT* pcSrcSize,
2820     BYTE* pDstStr,
2821     UINT* pcDstSize)
2822 {
2823     return ConvertINetString(pdwMode, dwSrcEncoding, dwDstEncoding,
2824         (LPCSTR)pSrcStr, (LPINT)pcSrcSize, (LPSTR)pDstStr, (LPINT)pcDstSize);
2825 }
2826 
2827 static HRESULT WINAPI fnIMultiLanguage3_ConvertStringToUnicode(
2828     IMultiLanguage3* iface,
2829     DWORD* pdwMode,
2830     DWORD dwEncoding,
2831     CHAR* pSrcStr,
2832     UINT* pcSrcSize,
2833     WCHAR* pDstStr,
2834     UINT* pcDstSize)
2835 {
2836     return ConvertINetMultiByteToUnicode(pdwMode, dwEncoding,
2837         pSrcStr, (LPINT)pcSrcSize, pDstStr, (LPINT)pcDstSize);
2838 }
2839 
2840 static HRESULT WINAPI fnIMultiLanguage3_ConvertStringFromUnicode(
2841     IMultiLanguage3* iface,
2842     DWORD* pdwMode,
2843     DWORD dwEncoding,
2844     WCHAR* pSrcStr,
2845     UINT* pcSrcSize,
2846     CHAR* pDstStr,
2847     UINT* pcDstSize)
2848 {
2849     return ConvertINetUnicodeToMultiByte(pdwMode, dwEncoding,
2850         pSrcStr, (LPINT)pcSrcSize, pDstStr, (LPINT)pcDstSize);
2851 }
2852 
2853 static HRESULT WINAPI fnIMultiLanguage3_ConvertStringReset(
2854     IMultiLanguage3* iface)
2855 {
2856     FIXME("\n");
2857     return E_NOTIMPL;
2858 }
2859 
2860 static HRESULT WINAPI fnIMultiLanguage3_GetRfc1766FromLcid(
2861     IMultiLanguage3* iface,
2862     LCID lcid,
2863     BSTR* pbstrRfc1766)
2864 {
2865     WCHAR buf[MAX_RFC1766_NAME];
2866 
2867     TRACE("%p %04x %p\n", iface, lcid, pbstrRfc1766);
2868     if (!pbstrRfc1766)
2869         return E_INVALIDARG;
2870 
2871     if (!lcid_to_rfc1766W( lcid, buf, MAX_RFC1766_NAME ))
2872     {
2873         *pbstrRfc1766 = SysAllocString( buf );
2874         return S_OK;
2875     }
2876     return E_FAIL;
2877 }
2878 
2879 static HRESULT WINAPI fnIMultiLanguage3_GetLcidFromRfc1766(
2880     IMultiLanguage3* iface,
2881     LCID* pLocale,
2882     BSTR bstrRfc1766)
2883 {
2884     HRESULT hr;
2885     IEnumRfc1766 *rfc1766;
2886 
2887     TRACE("%p %p %s\n", iface, pLocale, debugstr_w(bstrRfc1766));
2888 
2889     if (!pLocale || !bstrRfc1766)
2890         return E_INVALIDARG;
2891 
2892     hr = IMultiLanguage3_EnumRfc1766(iface, 0, &rfc1766);
2893     if (FAILED(hr))
2894         return hr;
2895 
2896     hr = lcid_from_rfc1766(rfc1766, pLocale, bstrRfc1766);
2897 
2898     IEnumRfc1766_Release(rfc1766);
2899     return hr;
2900 }
2901 
2902 static HRESULT WINAPI fnIMultiLanguage3_EnumRfc1766(
2903     IMultiLanguage3* iface,
2904     LANGID LangId,
2905     IEnumRfc1766** ppEnumRfc1766)
2906 {
2907     MLang_impl *This = impl_from_IMultiLanguage3( iface );
2908 
2909     TRACE("%p %p\n", This, ppEnumRfc1766);
2910 
2911     return EnumRfc1766_create(LangId, ppEnumRfc1766);
2912 }
2913 
2914 static HRESULT WINAPI fnIMultiLanguage3_GetRfc1766Info(
2915     IMultiLanguage3* iface,
2916     LCID Locale,
2917     LANGID LangId,
2918     PRFC1766INFO pRfc1766Info)
2919 {
2920     static LANGID last_lang = -1;
2921     LCTYPE type = LOCALE_SLANGUAGE;
2922 
2923     TRACE("(%p, 0x%04x, 0x%04x, %p)\n", iface, Locale, LangId, pRfc1766Info);
2924 
2925     if (!pRfc1766Info)
2926         return E_INVALIDARG;
2927 
2928     if ((PRIMARYLANGID(Locale) == LANG_ENGLISH) ||
2929         (PRIMARYLANGID(Locale) == LANG_CHINESE) ||
2930         (PRIMARYLANGID(Locale) == LANG_ARABIC)) {
2931 
2932         if (!SUBLANGID(Locale))
2933             type = LOCALE_SENGLANGUAGE; /* suppress country */
2934     }
2935     else
2936     {
2937         if (!SUBLANGID(Locale)) {
2938             TRACE("SUBLANGID missing in 0x%04x\n", Locale);
2939             return E_FAIL;
2940         }
2941     }
2942 
2943     pRfc1766Info->lcid = Locale;
2944     pRfc1766Info->wszRfc1766[0] = 0;
2945     pRfc1766Info->wszLocaleName[0] = 0;
2946 
2947     if ((PRIMARYLANGID(LangId) != LANG_ENGLISH) &&
2948         (last_lang != LangId)) {
2949         FIXME("Only English names supported (requested: 0x%04x)\n", LangId);
2950         last_lang = LangId;
2951     }
2952 
2953     if ((!lcid_to_rfc1766W(Locale, pRfc1766Info->wszRfc1766, MAX_RFC1766_NAME)) &&
2954         (GetLocaleInfoW(Locale, type, pRfc1766Info->wszLocaleName, MAX_LOCALE_NAME) > 0))
2955             return S_OK;
2956 
2957     /* Locale not supported */
2958     return E_INVALIDARG;
2959 }
2960 
2961 static HRESULT WINAPI fnIMultiLanguage3_CreateConvertCharset(
2962     IMultiLanguage3* iface,
2963     UINT src_cp,
2964     UINT dst_cp,
2965     DWORD prop,
2966     IMLangConvertCharset** convert_charset)
2967 {
2968     HRESULT hr;
2969 
2970     TRACE("(%u %u 0x%08x %p)\n", src_cp, dst_cp, prop, convert_charset);
2971 
2972     hr = MLangConvertCharset_create(NULL, (void**)convert_charset);
2973     if (FAILED(hr)) return hr;
2974 
2975     return IMLangConvertCharset_Initialize(*convert_charset, src_cp, dst_cp, prop);
2976 }
2977 
2978 static HRESULT WINAPI fnIMultiLanguage3_ConvertStringInIStream(
2979     IMultiLanguage3* iface,
2980     DWORD* pdwMode,
2981     DWORD dwFlag,
2982     WCHAR* lpFallBack,
2983     DWORD dwSrcEncoding,
2984     DWORD dwDstEncoding,
2985     IStream* pstmIn,
2986     IStream* pstmOut)
2987 {
2988     char *src, *dst = NULL;
2989     INT srclen, dstlen;
2990     STATSTG stat;
2991     HRESULT hr;
2992 
2993     TRACE("%p %0x8 %s %u %u %p %p\n",
2994           pdwMode, dwFlag, debugstr_w(lpFallBack), dwSrcEncoding, dwDstEncoding, pstmIn, pstmOut);
2995 
2996     FIXME("dwFlag and lpFallBack not handled\n");
2997 
2998     hr = IStream_Stat(pstmIn, &stat, STATFLAG_NONAME);
2999     if (FAILED(hr)) return hr;
3000 
3001     if (stat.cbSize.QuadPart > MAXLONG) return E_INVALIDARG;
3002     if (!(src = HeapAlloc(GetProcessHeap(), 0, stat.cbSize.QuadPart))) return E_OUTOFMEMORY;
3003 
3004     hr = IStream_Read(pstmIn, src, stat.cbSize.QuadPart, (ULONG *)&srclen);
3005     if (FAILED(hr)) goto exit;
3006 
3007     hr = ConvertINetString(pdwMode, dwSrcEncoding, dwDstEncoding, src, &srclen, NULL, &dstlen);
3008     if (FAILED(hr)) goto exit;
3009 
3010     if (!(dst = HeapAlloc(GetProcessHeap(), 0, dstlen)))
3011     {
3012         hr = E_OUTOFMEMORY;
3013         goto exit;
3014     }
3015     hr = ConvertINetString(pdwMode, dwSrcEncoding, dwDstEncoding, src, &srclen, dst, &dstlen);
3016     if (FAILED(hr)) goto exit;
3017 
3018     hr = IStream_Write(pstmOut, dst, dstlen, NULL);
3019 
3020 exit:
3021     HeapFree(GetProcessHeap(), 0, src);
3022     HeapFree(GetProcessHeap(), 0, dst);
3023     return hr;
3024 }
3025 
3026 static HRESULT WINAPI fnIMultiLanguage3_ConvertStringToUnicodeEx(
3027     IMultiLanguage3* iface,
3028     DWORD* pdwMode,
3029     DWORD dwEncoding,
3030     CHAR* pSrcStr,
3031     UINT* pcSrcSize,
3032     WCHAR* pDstStr,
3033     UINT* pcDstSize,
3034     DWORD dwFlag,
3035     WCHAR* lpFallBack)
3036 {
3037     if (dwFlag || lpFallBack)
3038         FIXME("Ignoring dwFlag (0x%x/%d) and lpFallBack (%p)\n",
3039                 dwFlag, dwFlag, lpFallBack);
3040 
3041     return ConvertINetMultiByteToUnicode(pdwMode, dwEncoding,
3042         pSrcStr, (LPINT)pcSrcSize, pDstStr, (LPINT)pcDstSize);
3043 }
3044 
3045 /*****************************************************************************
3046  * MultiLanguage2::ConvertStringToUnicodeEx
3047  *
3048  * Translates the multibyte string from the specified code page to Unicode.
3049  *
3050  * PARAMS
3051  *   see ConvertStringToUnicode
3052  *   dwFlag
3053  *   lpFallBack if dwFlag contains MLCONVCHARF_USEDEFCHAR, lpFallBack string used
3054  *              instead unconvertible characters.
3055  *
3056  * RETURNS
3057  *   S_OK     Success.
3058  *   S_FALSE  The conversion is not supported.
3059  *   E_FAIL   Some error has occurred.
3060  *
3061  * TODO: handle dwFlag and lpFallBack
3062 */
3063 static HRESULT WINAPI fnIMultiLanguage3_ConvertStringFromUnicodeEx(
3064     IMultiLanguage3* This,
3065     DWORD* pdwMode,
3066     DWORD dwEncoding,
3067     WCHAR* pSrcStr,
3068     UINT* pcSrcSize,
3069     CHAR* pDstStr,
3070     UINT* pcDstSize,
3071     DWORD dwFlag,
3072     WCHAR* lpFallBack)
3073 {
3074     FIXME("\n");
3075     return ConvertINetUnicodeToMultiByte(pdwMode, dwEncoding,
3076         pSrcStr, (LPINT)pcSrcSize, pDstStr, (LPINT)pcDstSize);
3077 }
3078 
3079 static HRESULT WINAPI fnIMultiLanguage3_DetectCodepageInIStream(
3080     IMultiLanguage3* iface,
3081     DWORD dwFlag,
3082     DWORD dwPrefWinCodePage,
3083     IStream* pstmIn,
3084     DetectEncodingInfo* lpEncoding,
3085     INT* pnScores)
3086 {
3087     FIXME("\n");
3088     return E_NOTIMPL;
3089 }
3090 
3091 static HRESULT WINAPI fnIMultiLanguage3_DetectInputCodepage(
3092     IMultiLanguage3* iface,
3093     DWORD dwFlag,
3094     DWORD dwPrefWinCodePage,
3095     CHAR* pSrcStr,
3096     INT* pcSrcSize,
3097     DetectEncodingInfo* lpEncoding,
3098     INT* pnScores)
3099 {
3100     FIXME("\n");
3101     return E_NOTIMPL;
3102 }
3103 
3104 static HRESULT WINAPI fnIMultiLanguage3_ValidateCodePage(
3105     IMultiLanguage3* iface,
3106     UINT uiCodePage,
3107     HWND hwnd)
3108 {
3109     return IMultiLanguage3_ValidateCodePageEx(iface,uiCodePage,hwnd,0);
3110 }
3111 
3112 static HRESULT WINAPI fnIMultiLanguage3_GetCodePageDescription(
3113     IMultiLanguage3* iface,
3114     UINT uiCodePage,
3115     LCID lcid,
3116     LPWSTR lpWideCharStr,
3117     int cchWideChar)
3118 {
3119     /* Find first instance */
3120     unsigned int i,n;
3121 
3122     TRACE ("%u, %04x, %p, %d\n", uiCodePage, lcid, lpWideCharStr, cchWideChar);
3123     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
3124     {
3125         for (n = 0; n < mlang_data[i].number_of_cp; n++)
3126         {
3127             if (mlang_data[i].mime_cp_info[n].cp == uiCodePage)
3128             {
3129                 MultiByteToWideChar(CP_ACP, 0,
3130                                     mlang_data[i].mime_cp_info[n].description,
3131                                     -1, lpWideCharStr, cchWideChar);
3132                 return S_OK;
3133             }
3134         }
3135     }
3136 
3137     return S_FALSE;
3138 }
3139 
3140 static HRESULT WINAPI fnIMultiLanguage3_IsCodePageInstallable(
3141     IMultiLanguage3* iface,
3142     UINT uiCodePage)
3143 {
3144     TRACE("%u\n", uiCodePage);
3145 
3146     /* FIXME: the installable set is usually larger than the set of valid codepages */
3147     return IMultiLanguage3_ValidateCodePageEx(iface, uiCodePage, NULL, CPIOD_PEEK);
3148 }
3149 
3150 static HRESULT WINAPI fnIMultiLanguage3_SetMimeDBSource(
3151     IMultiLanguage3* iface,
3152     MIMECONTF dwSource)
3153 {
3154     FIXME("0x%08x\n", dwSource);
3155     return S_OK;
3156 }
3157 
3158 static HRESULT WINAPI fnIMultiLanguage3_GetNumberOfScripts(
3159     IMultiLanguage3* iface,
3160     UINT* pnScripts)
3161 {
3162     MLang_impl *This = impl_from_IMultiLanguage3( iface );
3163 
3164     TRACE("%p %p\n", This, pnScripts);
3165 
3166     if (!pnScripts) return S_FALSE;
3167 
3168     *pnScripts = This->total_scripts;
3169     return S_OK;
3170 }
3171 
3172 static HRESULT WINAPI fnIMultiLanguage3_EnumScripts(
3173     IMultiLanguage3* iface,
3174     DWORD dwFlags,
3175     LANGID LangId,
3176     IEnumScript** ppEnumScript)
3177 {
3178     MLang_impl *This = impl_from_IMultiLanguage3( iface );
3179 
3180     TRACE("%p %08x %04x %p\n", This, dwFlags, LangId, ppEnumScript);
3181 
3182     return EnumScript_create( This, dwFlags, LangId, ppEnumScript );
3183 }
3184 
3185 static HRESULT WINAPI fnIMultiLanguage3_ValidateCodePageEx(
3186     IMultiLanguage3* iface,
3187     UINT uiCodePage,
3188     HWND hwnd,
3189     DWORD dwfIODControl)
3190 {
3191     unsigned int i;
3192     MLang_impl *This = impl_from_IMultiLanguage3( iface );
3193 
3194     TRACE("%p %u %p %08x\n", This, uiCodePage, hwnd, dwfIODControl);
3195 
3196     /* quick check for kernel32 supported code pages */
3197     if (IsValidCodePage(uiCodePage))
3198         return S_OK;
3199 
3200     /* check for mlang supported code pages */
3201     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
3202     {
3203         UINT n;
3204         for (n = 0; n < mlang_data[i].number_of_cp; n++)
3205         {
3206             if (mlang_data[i].mime_cp_info[n].cp == uiCodePage)
3207                 return S_OK;
3208         }
3209     }
3210 
3211     if (dwfIODControl != CPIOD_PEEK)
3212         FIXME("Request to install codepage language pack not handled\n");
3213 
3214     return S_FALSE;
3215 }
3216 
3217 static HRESULT WINAPI fnIMultiLanguage3_DetectOutboundCodePage(
3218     IMultiLanguage3 *iface,
3219     DWORD dwFlags,
3220     LPCWSTR lpWideCharStr,
3221     UINT cchWideChar,
3222     UINT *puiPreferredCodePages,
3223     UINT nPreferredCodePages,
3224     UINT *puiDetectedCodePages,
3225     UINT *pnDetectedCodePages,
3226     WCHAR *lpSpecialChar)
3227 {
3228     MLang_impl *This = impl_from_IMultiLanguage3( iface );
3229 
3230     FIXME("(%p)->(%08x %s %p %u %p %p(%u) %s)\n", This, dwFlags, debugstr_w(lpWideCharStr),
3231           puiPreferredCodePages, nPreferredCodePages, puiDetectedCodePages,
3232           pnDetectedCodePages, pnDetectedCodePages ? *pnDetectedCodePages : 0,
3233           debugstr_w(lpSpecialChar));
3234 
3235     if (!puiDetectedCodePages || !pnDetectedCodePages || !*pnDetectedCodePages)
3236         return E_INVALIDARG;
3237 
3238     puiDetectedCodePages[0] = CP_UTF8;
3239     *pnDetectedCodePages = 1;
3240     return S_OK;
3241 }
3242 
3243 static HRESULT WINAPI fnIMultiLanguage3_DetectOutboundCodePageInIStream(
3244     IMultiLanguage3 *iface,
3245     DWORD dwFlags,
3246     IStream *pStrIn,
3247     UINT *puiPreferredCodePages,
3248     UINT nPreferredCodePages,
3249     UINT *puiDetectedCodePages,
3250     UINT *pnDetectedCodePages,
3251     WCHAR *lpSpecialChar)
3252 {
3253     MLang_impl *This = impl_from_IMultiLanguage3( iface );
3254 
3255     FIXME("(%p)->(%08x %p %p %u %p %p(%u) %s)\n", This, dwFlags, pStrIn,
3256           puiPreferredCodePages, nPreferredCodePages, puiDetectedCodePages,
3257           pnDetectedCodePages, pnDetectedCodePages ? *pnDetectedCodePages : 0,
3258           debugstr_w(lpSpecialChar));
3259 
3260     if (!puiDetectedCodePages || !pnDetectedCodePages || !*pnDetectedCodePages)
3261         return E_INVALIDARG;
3262 
3263     puiDetectedCodePages[0] = CP_UTF8;
3264     *pnDetectedCodePages = 1;
3265     return S_OK;
3266 }
3267 
3268 static const IMultiLanguage3Vtbl IMultiLanguage3_vtbl =
3269 {
3270     fnIMultiLanguage3_QueryInterface,
3271     fnIMultiLanguage3_AddRef,
3272     fnIMultiLanguage3_Release,
3273     fnIMultiLanguage3_GetNumberOfCodePageInfo,
3274     fnIMultiLanguage3_GetCodePageInfo,
3275     fnIMultiLanguage3_GetFamilyCodePage,
3276     fnIMultiLanguage3_EnumCodePages,
3277     fnIMultiLanguage3_GetCharsetInfo,
3278     fnIMultiLanguage3_IsConvertible,
3279     fnIMultiLanguage3_ConvertString,
3280     fnIMultiLanguage3_ConvertStringToUnicode,
3281     fnIMultiLanguage3_ConvertStringFromUnicode,
3282     fnIMultiLanguage3_ConvertStringReset,
3283     fnIMultiLanguage3_GetRfc1766FromLcid,
3284     fnIMultiLanguage3_GetLcidFromRfc1766,
3285     fnIMultiLanguage3_EnumRfc1766,
3286     fnIMultiLanguage3_GetRfc1766Info,
3287     fnIMultiLanguage3_CreateConvertCharset,
3288     fnIMultiLanguage3_ConvertStringInIStream,
3289     fnIMultiLanguage3_ConvertStringToUnicodeEx,
3290     fnIMultiLanguage3_ConvertStringFromUnicodeEx,
3291     fnIMultiLanguage3_DetectCodepageInIStream,
3292     fnIMultiLanguage3_DetectInputCodepage,
3293     fnIMultiLanguage3_ValidateCodePage,
3294     fnIMultiLanguage3_GetCodePageDescription,
3295     fnIMultiLanguage3_IsCodePageInstallable,
3296     fnIMultiLanguage3_SetMimeDBSource,
3297     fnIMultiLanguage3_GetNumberOfScripts,
3298     fnIMultiLanguage3_EnumScripts,
3299     fnIMultiLanguage3_ValidateCodePageEx,
3300     fnIMultiLanguage3_DetectOutboundCodePage,
3301     fnIMultiLanguage3_DetectOutboundCodePageInIStream
3302 };
3303 
3304 /******************************************************************************/
3305 
3306 static inline MLang_impl *impl_from_IMLangFontLink2( IMLangFontLink2 *iface )
3307 {
3308     return CONTAINING_RECORD( iface, MLang_impl, IMLangFontLink2_iface );
3309 }
3310 
3311 static HRESULT WINAPI fnIMLangFontLink2_QueryInterface(
3312     IMLangFontLink2 * iface,
3313     REFIID riid,
3314     void** ppvObject)
3315 {
3316     MLang_impl *This = impl_from_IMLangFontLink2( iface );
3317     return IMultiLanguage3_QueryInterface( &This->IMultiLanguage3_iface, riid, ppvObject );
3318 }
3319 
3320 static ULONG WINAPI fnIMLangFontLink2_AddRef( IMLangFontLink2* iface )
3321 {
3322     MLang_impl *This = impl_from_IMLangFontLink2( iface );
3323     return IMultiLanguage3_AddRef( &This->IMultiLanguage3_iface );
3324 }
3325 
3326 static ULONG WINAPI fnIMLangFontLink2_Release( IMLangFontLink2* iface )
3327 {
3328     MLang_impl *This = impl_from_IMLangFontLink2( iface );
3329     return IMultiLanguage3_Release( &This->IMultiLanguage3_iface );
3330 }
3331 
3332 static HRESULT WINAPI fnIMLangFontLink2_GetCharCodePages( IMLangFontLink2* iface,
3333         WCHAR ch_src, DWORD *ret_codepages)
3334 {
3335     MLang_impl *This = impl_from_IMLangFontLink2(iface);
3336     unsigned int i;
3337 
3338     TRACE("(%p)->(%s %p)\n", This, debugstr_wn(&ch_src, 1), ret_codepages);
3339 
3340     *ret_codepages = 0;
3341 
3342     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
3343     {
3344         BOOL used_dc;
3345         CHAR buf;
3346 
3347         WideCharToMultiByte(mlang_data[i].family_codepage, WC_NO_BEST_FIT_CHARS,
3348             &ch_src, 1, &buf, 1, NULL, &used_dc);
3349 
3350         /* If default char is not used, current codepage include the given symbol */
3351         if (!used_dc)
3352         {
3353             DWORD codepages;
3354 
3355             IMLangFontLink2_CodePageToCodePages(iface,
3356                 mlang_data[i].family_codepage, &codepages);
3357             *ret_codepages |= codepages;
3358         }
3359     }
3360     return S_OK;
3361 }
3362 
3363 static HRESULT WINAPI fnIMLangFontLink2_GetStrCodePages( IMLangFontLink2* iface,
3364         const WCHAR *src, LONG src_len, DWORD priority_cp,
3365         DWORD *codepages, LONG *ret_len)
3366 {
3367     MLang_impl *This = impl_from_IMLangFontLink2(iface);
3368     LONG i;
3369     DWORD cps = 0;
3370 
3371     TRACE("(%p)->(%s:%d %x %p %p)\n", This, debugstr_wn(src, src_len), src_len, priority_cp,
3372         codepages, ret_len);
3373 
3374     if (codepages) *codepages = 0;
3375     if (ret_len) *ret_len = 0;
3376 
3377     if (!src || !src_len || src_len < 0)
3378         return E_INVALIDARG;
3379 
3380     for (i = 0; i < src_len; i++)
3381     {
3382         DWORD cp;
3383         HRESULT ret;
3384 
3385         ret = IMLangFontLink2_GetCharCodePages(iface, src[i], &cp);
3386         if (ret != S_OK) return E_FAIL;
3387 
3388         if (!cps) cps = cp;
3389         else cps &= cp;
3390 
3391         /* FIXME: not tested */
3392         if (priority_cp & cps) break;
3393     }
3394 
3395     if (codepages) *codepages = cps;
3396     if (ret_len) *ret_len = min( i + 1, src_len );
3397     return S_OK;
3398 }
3399 
3400 static HRESULT WINAPI fnIMLangFontLink2_CodePageToCodePages(IMLangFontLink2* iface,
3401         UINT codepage,
3402         DWORD *codepages)
3403 {
3404     MLang_impl *This = impl_from_IMLangFontLink2(iface);
3405     CHARSETINFO cs;
3406     BOOL rc;
3407 
3408     TRACE("(%p)->(%u %p)\n", This, codepage, codepages);
3409 
3410     rc = TranslateCharsetInfo((DWORD*)(DWORD_PTR)codepage, &cs, TCI_SRCCODEPAGE);
3411     if (rc)
3412     {
3413         *codepages = cs.fs.fsCsb[0];
3414         TRACE("resulting codepages 0x%x\n", *codepages);
3415         return S_OK;
3416     }
3417 
3418     TRACE("codepage not found\n");
3419     *codepages = 0;
3420     return E_FAIL;
3421 }
3422 
3423 static HRESULT WINAPI fnIMLangFontLink2_CodePagesToCodePage(IMLangFontLink2* iface,
3424         DWORD codepages, UINT def_codepage, UINT *codepage)
3425 {
3426     MLang_impl *This = impl_from_IMLangFontLink2(iface);
3427     DWORD mask = 0;
3428     CHARSETINFO cs;
3429     BOOL rc;
3430     UINT i;
3431 
3432     TRACE("(%p)->(0x%x %u %p)\n", This, codepages, def_codepage, codepage);
3433 
3434     *codepage = 0;
3435 
3436     rc = TranslateCharsetInfo((DWORD*)(DWORD_PTR)def_codepage, &cs, TCI_SRCCODEPAGE);
3437     if (rc && (codepages & cs.fs.fsCsb[0]))
3438     {
3439         TRACE("Found Default Codepage\n");
3440         *codepage = def_codepage;
3441         return S_OK;
3442     }
3443 
3444     for (i = 0; i < 32; i++)
3445     {
3446         mask = 1 << i;
3447         if (codepages & mask)
3448         {
3449             DWORD Csb[2];
3450             Csb[0] = mask;
3451             Csb[1] = 0x0;
3452             rc = TranslateCharsetInfo(Csb, &cs, TCI_SRCFONTSIG);
3453             if (!rc)
3454                 continue;
3455 
3456             TRACE("Falling back to least significant found CodePage %u\n",
3457                     cs.ciACP);
3458             *codepage = cs.ciACP;
3459             return S_OK;
3460         }
3461     }
3462 
3463     TRACE("no codepage found\n");
3464     return E_FAIL;
3465 }
3466 
3467 static HRESULT WINAPI fnIMLangFontLink2_GetFontCodePages(IMLangFontLink2 *iface,
3468         HDC hdc, HFONT hfont, DWORD *codepages)
3469 {
3470     MLang_impl *This = impl_from_IMLangFontLink2(iface);
3471     FONTSIGNATURE fontsig;
3472     HFONT old_font;
3473 
3474     TRACE("(%p)->(%p %p %p)\n", This, hdc, hfont, codepages);
3475 
3476     old_font = SelectObject(hdc, hfont);
3477     GetTextCharsetInfo(hdc, &fontsig, 0);
3478     SelectObject(hdc, old_font);
3479 
3480     *codepages = fontsig.fsCsb[0];
3481     TRACE("ret 0x%x\n", fontsig.fsCsb[0]);
3482 
3483     return S_OK;
3484 }
3485 
3486 static HRESULT WINAPI fnIMLangFontLink2_ReleaseFont(IMLangFontLink2* This,
3487         HFONT hFont)
3488 {
3489     TRACE("(%p)->%p\n",This, hFont);
3490 
3491     return release_font(hFont);
3492 }
3493 
3494 static HRESULT WINAPI fnIMLangFontLink2_ResetFontMapping(IMLangFontLink2* This)
3495 {
3496     TRACE("(%p)\n",This);
3497 
3498     return clear_font_cache();
3499 }
3500 
3501 static HRESULT WINAPI fnIMLangFontLink2_MapFont(IMLangFontLink2* This,
3502         HDC hDC, DWORD dwCodePages, WCHAR chSrc, HFONT *pFont)
3503 {
3504     HFONT old_font;
3505 
3506     TRACE("(%p)->%p %08x %04x %p\n",This, hDC, dwCodePages, chSrc, pFont);
3507 
3508     if (!hDC) return E_FAIL;
3509 
3510     if (dwCodePages != 0)
3511     {
3512         old_font = GetCurrentObject(hDC, OBJ_FONT);
3513         return map_font(hDC, dwCodePages, old_font, pFont);
3514     }
3515     else
3516     {
3517         if (pFont == NULL) return E_INVALIDARG;
3518         FIXME("the situation where dwCodepages is set to zero is not implemented\n");
3519         return E_FAIL;
3520     }
3521 }
3522 
3523 static HRESULT WINAPI fnIMLangFontLink2_GetFontUnicodeRanges(IMLangFontLink2* This,
3524         HDC hDC, UINT *puiRanges, UNICODERANGE *pUranges)
3525 {
3526     DWORD size;
3527     GLYPHSET *gs;
3528 
3529     TRACE("(%p)->%p %p %p\n", This, hDC, puiRanges, pUranges);
3530 
3531     if (!puiRanges) return E_INVALIDARG;
3532     if (!(size = GetFontUnicodeRanges(hDC, NULL))) return E_FAIL;
3533     if (!(gs = HeapAlloc(GetProcessHeap(), 0, size))) return E_OUTOFMEMORY;
3534 
3535     GetFontUnicodeRanges(hDC, gs);
3536     *puiRanges = gs->cRanges;
3537     if (pUranges)
3538     {
3539         UINT i;
3540         for (i = 0; i < gs->cRanges; i++)
3541         {
3542             if (i >= *puiRanges) break;
3543             pUranges[i].wcFrom = gs->ranges[i].wcLow;
3544             pUranges[i].wcTo   = gs->ranges[i].wcLow + gs->ranges[i].cGlyphs;
3545         }
3546         *puiRanges = i;
3547     }
3548     HeapFree(GetProcessHeap(), 0, gs);
3549     return S_OK;
3550 }
3551 
3552 static HRESULT WINAPI fnIMLangFontLink2_GetScriptFontInfo(IMLangFontLink2* This,
3553         SCRIPT_ID sid, DWORD dwFlags, UINT *puiFonts,
3554         SCRIPTFONTINFO *pScriptFont)
3555 {
3556     UINT i, j;
3557 
3558     TRACE("(%p)->%u %x %p %p\n", This, sid, dwFlags, puiFonts, pScriptFont);
3559 
3560     if (!dwFlags) dwFlags = SCRIPTCONTF_PROPORTIONAL_FONT;
3561 
3562     for (i = 0, j = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
3563     {
3564         if (sid == mlang_data[i].sid)
3565         {
3566             if (pScriptFont)
3567             {
3568                 if (j >= *puiFonts) break;
3569 
3570                 pScriptFont[j].scripts = 1 << mlang_data[i].sid;
3571                 if (dwFlags == SCRIPTCONTF_FIXED_FONT)
3572                 {
3573                     MultiByteToWideChar(CP_ACP, 0, mlang_data[i].fixed_font, -1,
3574                         pScriptFont[j].wszFont, MAX_MIMEFACE_NAME);
3575                 }
3576                 else if (dwFlags == SCRIPTCONTF_PROPORTIONAL_FONT)
3577                 {
3578                     MultiByteToWideChar(CP_ACP, 0, mlang_data[i].proportional_font, -1,
3579                         pScriptFont[j].wszFont, MAX_MIMEFACE_NAME);
3580                 }
3581             }
3582             j++;
3583         }
3584     }
3585     *puiFonts = j;
3586     return S_OK;
3587 }
3588 
3589 static HRESULT WINAPI fnIMLangFontLink2_CodePageToScriptID(IMLangFontLink2* This,
3590         UINT uiCodePage, SCRIPT_ID *pSid)
3591 {
3592     UINT i;
3593 
3594     TRACE("(%p)->%i %p\n", This, uiCodePage, pSid);
3595 
3596     if (uiCodePage == CP_UNICODE) return E_FAIL;
3597 
3598     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
3599     {
3600         if (uiCodePage == mlang_data[i].family_codepage)
3601         {
3602             if (pSid) *pSid = mlang_data[i].sid;
3603             return S_OK;
3604         }
3605     }
3606     return E_FAIL;
3607 }
3608 
3609 static const IMLangFontLink2Vtbl IMLangFontLink2_vtbl =
3610 {
3611     fnIMLangFontLink2_QueryInterface,
3612     fnIMLangFontLink2_AddRef,
3613     fnIMLangFontLink2_Release,
3614     fnIMLangFontLink2_GetCharCodePages,
3615     fnIMLangFontLink2_GetStrCodePages,
3616     fnIMLangFontLink2_CodePageToCodePages,
3617     fnIMLangFontLink2_CodePagesToCodePage,
3618     fnIMLangFontLink2_GetFontCodePages,
3619     fnIMLangFontLink2_ReleaseFont,
3620     fnIMLangFontLink2_ResetFontMapping,
3621     fnIMLangFontLink2_MapFont,
3622     fnIMLangFontLink2_GetFontUnicodeRanges,
3623     fnIMLangFontLink2_GetScriptFontInfo,
3624     fnIMLangFontLink2_CodePageToScriptID
3625 };
3626 
3627 /******************************************************************************/
3628 
3629 static inline MLang_impl *impl_from_IMLangLineBreakConsole( IMLangLineBreakConsole *iface )
3630 {
3631     return CONTAINING_RECORD( iface, MLang_impl, IMLangLineBreakConsole_iface );
3632 }
3633 
3634 static HRESULT WINAPI fnIMLangLineBreakConsole_QueryInterface(
3635     IMLangLineBreakConsole* iface,
3636     REFIID riid,
3637     void** ppvObject)
3638 {
3639     MLang_impl *This = impl_from_IMLangLineBreakConsole( iface );
3640     return IMultiLanguage3_QueryInterface( &This->IMultiLanguage3_iface, riid, ppvObject );
3641 }
3642 
3643 static ULONG WINAPI fnIMLangLineBreakConsole_AddRef(
3644     IMLangLineBreakConsole* iface )
3645 {
3646     MLang_impl *This = impl_from_IMLangLineBreakConsole( iface );
3647     return IMultiLanguage3_AddRef( &This->IMultiLanguage3_iface );
3648 }
3649 
3650 static ULONG WINAPI fnIMLangLineBreakConsole_Release(
3651     IMLangLineBreakConsole* iface )
3652 {
3653     MLang_impl *This = impl_from_IMLangLineBreakConsole( iface );
3654     return IMultiLanguage3_Release( &This->IMultiLanguage3_iface );
3655 }
3656 
3657 static HRESULT WINAPI fnIMLangLineBreakConsole_BreakLineML(
3658     IMLangLineBreakConsole* iface,
3659     IMLangString* pSrcMLStr,
3660     LONG lSrcPos,
3661     LONG lSrcLen,
3662     LONG cMinColumns,
3663     LONG cMaxColumns,
3664     LONG* plLineLen,
3665     LONG* plSkipLen)
3666 {
3667     FIXME("(%p)->%p %i %i %i %i %p %p\n", iface, pSrcMLStr, lSrcPos, lSrcLen, cMinColumns, cMaxColumns, plLineLen, plSkipLen);
3668     return E_NOTIMPL;
3669 }
3670 
3671 static HRESULT WINAPI fnIMLangLineBreakConsole_BreakLineW(
3672     IMLangLineBreakConsole* iface,
3673     LCID locale,
3674     const WCHAR* pszSrc,
3675     LONG cchSrc,
3676     LONG cMaxColumns,
3677     LONG* pcchLine,
3678     LONG* pcchSkip )
3679 {
3680     FIXME("(%p)->%i %s %i %i %p %p\n", iface, locale, debugstr_wn(pszSrc,cchSrc), cchSrc, cMaxColumns, pcchLine, pcchSkip);
3681 
3682     *pcchLine = cchSrc;
3683     *pcchSkip = 0;
3684     return S_OK;
3685 }
3686 
3687 static HRESULT WINAPI fnIMLangLineBreakConsole_BreakLineA(
3688     IMLangLineBreakConsole* iface,
3689     LCID locale,
3690     UINT uCodePage,
3691     const CHAR* pszSrc,
3692     LONG cchSrc,
3693     LONG cMaxColumns,
3694     LONG* pcchLine,
3695     LONG* pcchSkip)
3696 {
3697     LONG i, line = cchSrc, skip = 0;
3698 
3699     FIXME("(%p)->%i %i %s %i %i %p %p\n", iface, locale, uCodePage, debugstr_an(pszSrc,cchSrc), cchSrc, cMaxColumns, pcchLine, pcchSkip);
3700 
3701     if (uCodePage == CP_USASCII && cchSrc > cMaxColumns)
3702     {
3703         for (line = cMaxColumns, i = cMaxColumns - 1; i >= 0; i--)
3704         {
3705             if (pszSrc[i] == ' ')
3706             {
3707                 while (i >= 0 && pszSrc[i] == ' ')
3708                 {
3709                     i--;
3710                     line--;
3711                     skip++;
3712                 }
3713                 break;
3714             }
3715         }
3716     }
3717     *pcchLine = line;
3718     *pcchSkip = skip;
3719     return S_OK;
3720 }
3721 
3722 static const IMLangLineBreakConsoleVtbl IMLangLineBreakConsole_vtbl =
3723 {
3724     fnIMLangLineBreakConsole_QueryInterface,
3725     fnIMLangLineBreakConsole_AddRef,
3726     fnIMLangLineBreakConsole_Release,
3727     fnIMLangLineBreakConsole_BreakLineML,
3728     fnIMLangLineBreakConsole_BreakLineW,
3729     fnIMLangLineBreakConsole_BreakLineA
3730 };
3731 
3732 struct convert_charset {
3733     IMLangConvertCharset IMLangConvertCharset_iface;
3734     LONG ref;
3735 
3736     UINT src_cp;
3737     UINT dst_cp;
3738 };
3739 
3740 static inline struct convert_charset *impl_from_IMLangConvertCharset(IMLangConvertCharset *iface)
3741 {
3742     return CONTAINING_RECORD(iface, struct convert_charset, IMLangConvertCharset_iface);
3743 }
3744 
3745 static HRESULT WINAPI MLangConvertCharset_QueryInterface(IMLangConvertCharset *iface, REFIID riid, void **obj)
3746 {
3747     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3748 
3749     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
3750 
3751     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IMLangConvertCharset))
3752     {
3753         *obj = &This->IMLangConvertCharset_iface;
3754         IMLangConvertCharset_AddRef(iface);
3755         return S_OK;
3756     }
3757 
3758     *obj = NULL;
3759     return E_NOINTERFACE;
3760 }
3761 
3762 static ULONG WINAPI MLangConvertCharset_AddRef(IMLangConvertCharset *iface)
3763 {
3764     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3765     ULONG ref = InterlockedIncrement(&This->ref);
3766     TRACE("(%p)->(%u)\n", This, ref);
3767     return ref;
3768 }
3769 
3770 static ULONG WINAPI MLangConvertCharset_Release(IMLangConvertCharset *iface)
3771 {
3772     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3773     ULONG ref = InterlockedDecrement(&This->ref);
3774 
3775     TRACE("(%p)->(%u)\n", This, ref);
3776     if (!ref)
3777     {
3778         HeapFree(GetProcessHeap(), 0, This);
3779         UnlockModule();
3780     }
3781 
3782     return ref;
3783 }
3784 
3785 static HRESULT WINAPI MLangConvertCharset_Initialize(IMLangConvertCharset *iface,
3786     UINT src_cp, UINT dst_cp, DWORD prop)
3787 {
3788     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3789 
3790     TRACE("(%p)->(%u %u 0x%08x)\n", This, src_cp, dst_cp, prop);
3791 
3792     prop &= ~MLCONVCHARF_USEDEFCHAR;
3793     if (prop)
3794         FIXME("property 0x%08x not supported\n", prop);
3795 
3796     This->src_cp = src_cp;
3797     This->dst_cp = dst_cp;
3798 
3799     return S_OK;
3800 }
3801 
3802 static HRESULT WINAPI MLangConvertCharset_GetSourceCodePage(IMLangConvertCharset *iface, UINT *src_cp)
3803 {
3804     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3805 
3806     TRACE("(%p)->(%p)\n", This, src_cp);
3807 
3808     if (!src_cp) return E_INVALIDARG;
3809     *src_cp = This->src_cp;
3810     return S_OK;
3811 }
3812 
3813 static HRESULT WINAPI MLangConvertCharset_GetDestinationCodePage(IMLangConvertCharset *iface, UINT *dst_cp)
3814 {
3815     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3816 
3817     TRACE("(%p)->(%p)\n", This, dst_cp);
3818 
3819     if (!dst_cp) return E_INVALIDARG;
3820     *dst_cp = This->dst_cp;
3821     return S_OK;
3822 }
3823 
3824 static HRESULT WINAPI MLangConvertCharset_GetProperty(IMLangConvertCharset *iface, DWORD *prop)
3825 {
3826     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3827     FIXME("(%p)->(%p): stub\n", This, prop);
3828     return E_NOTIMPL;
3829 }
3830 
3831 static HRESULT WINAPI MLangConvertCharset_DoConversion(IMLangConvertCharset *iface, BYTE *src,
3832     UINT *src_size, BYTE *dest, UINT *dest_size)
3833 {
3834     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3835     FIXME("(%p)->(%p %p %p %p): stub\n", This, src, src_size, dest, dest_size);
3836     return E_NOTIMPL;
3837 }
3838 
3839 static HRESULT WINAPI MLangConvertCharset_DoConversionToUnicode(IMLangConvertCharset *iface, CHAR *src,
3840     UINT *src_size, WCHAR *dest, UINT *dest_size)
3841 {
3842     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3843     TRACE("(%p)->(%p %p %p %p)\n", This, src, src_size, dest, dest_size);
3844     return ConvertINetMultiByteToUnicode(NULL, This->src_cp, src, (INT*)src_size, dest, (INT*)dest_size);
3845 }
3846 
3847 static HRESULT WINAPI MLangConvertCharset_DoConversionFromUnicode(IMLangConvertCharset *iface,
3848     WCHAR *src, UINT *src_size, CHAR *dest, UINT *dest_size)
3849 {
3850     struct convert_charset *This = impl_from_IMLangConvertCharset(iface);
3851     TRACE("(%p)->(%p %p %p %p)\n", This, src, src_size, dest, dest_size);
3852     return ConvertINetUnicodeToMultiByte(NULL, This->dst_cp, src, (INT*)src_size, dest, (INT*)dest_size);
3853 }
3854 
3855 static const IMLangConvertCharsetVtbl MLangConvertCharsetVtbl =
3856 {
3857     MLangConvertCharset_QueryInterface,
3858     MLangConvertCharset_AddRef,
3859     MLangConvertCharset_Release,
3860     MLangConvertCharset_Initialize,
3861     MLangConvertCharset_GetSourceCodePage,
3862     MLangConvertCharset_GetDestinationCodePage,
3863     MLangConvertCharset_GetProperty,
3864     MLangConvertCharset_DoConversion,
3865     MLangConvertCharset_DoConversionToUnicode,
3866     MLangConvertCharset_DoConversionFromUnicode
3867 };
3868 
3869 static HRESULT MultiLanguage_create(IUnknown *pUnkOuter, LPVOID *ppObj)
3870 {
3871     MLang_impl *mlang;
3872     UINT i;
3873 
3874     TRACE("Creating MultiLanguage object\n");
3875 
3876     if( pUnkOuter )
3877         return CLASS_E_NOAGGREGATION;
3878 
3879     mlang = HeapAlloc( GetProcessHeap(), 0, sizeof (MLang_impl) );
3880     mlang->IMLangFontLink_iface.lpVtbl = &IMLangFontLink_vtbl;
3881     mlang->IMultiLanguage_iface.lpVtbl = &IMultiLanguage_vtbl;
3882     mlang->IMultiLanguage3_iface.lpVtbl = &IMultiLanguage3_vtbl;
3883     mlang->IMLangFontLink2_iface.lpVtbl = &IMLangFontLink2_vtbl;
3884     mlang->IMLangLineBreakConsole_iface.lpVtbl = &IMLangLineBreakConsole_vtbl;
3885 
3886     mlang->total_cp = 0;
3887     for (i = 0; i < sizeof(mlang_data)/sizeof(mlang_data[0]); i++)
3888         mlang->total_cp += mlang_data[i].number_of_cp;
3889 
3890     /* do not enumerate unicode flavours */
3891     mlang->total_scripts = sizeof(mlang_data)/sizeof(mlang_data[0]) - 1;
3892 
3893     mlang->ref = 1;
3894     *ppObj = &mlang->IMultiLanguage_iface;
3895     TRACE("returning %p\n", mlang);
3896 
3897     LockModule();
3898 
3899     return S_OK;
3900 }
3901 
3902 static HRESULT MLangConvertCharset_create(IUnknown *outer, void **obj)
3903 {
3904     struct convert_charset *convert;
3905 
3906     if (outer)
3907         return CLASS_E_NOAGGREGATION;
3908 
3909     *obj = NULL;
3910 
3911     convert = HeapAlloc(GetProcessHeap(), 0, sizeof(struct convert_charset));
3912     if (!convert) return E_OUTOFMEMORY;
3913 
3914     convert->IMLangConvertCharset_iface.lpVtbl = &MLangConvertCharsetVtbl;
3915     convert->ref = 1;
3916 
3917     *obj = &convert->IMLangConvertCharset_iface;
3918 
3919     LockModule();
3920 
3921     return S_OK;
3922 }
3923 
3924 /******************************************************************************/
3925 
3926 HRESULT WINAPI DllCanUnloadNow(void)
3927 {
3928     return dll_count == 0 ? S_OK : S_FALSE;
3929 }
3930 
3931 
3932 /***********************************************************************
3933  *		DllRegisterServer (MLANG.@)
3934  */
3935 HRESULT WINAPI DllRegisterServer(void)
3936 {
3937     return __wine_register_resources( instance );
3938 }
3939 
3940 /***********************************************************************
3941  *		DllUnregisterServer (MLANG.@)
3942  */
3943 HRESULT WINAPI DllUnregisterServer(void)
3944 {
3945     return __wine_unregister_resources( instance );
3946 }
3947 
3948 HRESULT WINAPI GetGlobalFontLinkObject(void **unknown)
3949 {
3950     if (!unknown) return E_INVALIDARG;
3951 
3952     FIXME("%p: stub\n", unknown);
3953 
3954     return S_FALSE;
3955 }
3956