xref: /reactos/base/setup/lib/mui.c (revision 0b366ea1)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2008 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License along
16  *  with this program; if not, write to the Free Software Foundation, Inc.,
17  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 /*
20  * COPYRIGHT:       See COPYING in the top level directory
21  * PROJECT:         ReactOS text-mode setup
22  * FILE:            base/setup/usetup/mui.c
23  * PURPOSE:         Text-mode setup
24  * PROGRAMMER:
25  */
26 
27 /* INCLUDES *****************************************************************/
28 
29 #include "precomp.h"
30 #include "mui.h"
31 #include "muifonts.h"
32 #include "muilanguages.h"
33 #include "registry.h"
34 #include "substset.h"
35 
36 #define NDEBUG
37 #include <debug.h>
38 
39 
40 /* FUNCTIONS ****************************************************************/
41 
42 static
43 ULONG
44 FindLanguageIndex(
45     IN PCWSTR LanguageId)
46 {
47     ULONG lngIndex = 0;
48 
49     if (LanguageId == NULL)
50     {
51         /* Default to en-US */
52         // return 0;   // FIXME!!
53         LanguageId = L"00000409";
54     }
55 
56     while (MUILanguageList[lngIndex].LanguageID != NULL)
57     {
58         if (_wcsicmp(MUILanguageList[lngIndex].LanguageID, LanguageId) == 0)
59         {
60             return lngIndex;
61         }
62 
63         lngIndex++;
64     }
65 
66     return 0;
67 }
68 
69 BOOLEAN
70 IsLanguageAvailable(
71     IN PCWSTR LanguageId)
72 {
73     ULONG lngIndex = 0;
74 
75     while (MUILanguageList[lngIndex].LanguageID != NULL)
76     {
77         if (_wcsicmp(MUILanguageList[lngIndex].LanguageID, LanguageId) == 0)
78             return TRUE;
79 
80         lngIndex++;
81     }
82 
83     return FALSE;
84 }
85 
86 
87 KLID
88 MUIDefaultKeyboardLayout(
89     IN PCWSTR LanguageId)
90 {
91     ULONG lngIndex = FindLanguageIndex(LanguageId);
92     return MUILanguageList[lngIndex].MuiLayouts[0].LayoutID;
93 }
94 
95 UINT
96 MUIGetOEMCodePage(
97     IN PCWSTR LanguageId)
98 {
99     ULONG lngIndex = FindLanguageIndex(LanguageId);
100     return MUILanguageList[lngIndex].OEMCPage;
101 }
102 
103 GEOID
104 MUIGetGeoID(
105     IN PCWSTR LanguageId)
106 {
107     ULONG lngIndex = FindLanguageIndex(LanguageId);
108     return MUILanguageList[lngIndex].GeoID;
109 }
110 
111 const MUI_LAYOUTS*
112 MUIGetLayoutsList(
113     IN PCWSTR LanguageId)
114 {
115     ULONG lngIndex = FindLanguageIndex(LanguageId);
116     return MUILanguageList[lngIndex].MuiLayouts;
117 }
118 
119 
120 static
121 BOOLEAN
122 AddHotkeySettings(
123     IN PCWSTR Hotkey,
124     IN PCWSTR LangHotkey,
125     IN PCWSTR LayoutHotkey)
126 {
127     OBJECT_ATTRIBUTES ObjectAttributes;
128     UNICODE_STRING KeyName;
129     UNICODE_STRING ValueName;
130     HANDLE KeyHandle;
131     ULONG Disposition;
132     NTSTATUS Status;
133 
134     RtlInitUnicodeString(&KeyName,
135                          L".DEFAULT\\Keyboard Layout\\Toggle");
136     InitializeObjectAttributes(&ObjectAttributes,
137                                &KeyName,
138                                OBJ_CASE_INSENSITIVE,
139                                GetRootKeyByPredefKey(HKEY_USERS, NULL),
140                                NULL);
141 
142     Status =  NtCreateKey(&KeyHandle,
143                           KEY_SET_VALUE,
144                           &ObjectAttributes,
145                           0,
146                           NULL,
147                           REG_OPTION_NON_VOLATILE,
148                           &Disposition);
149     if (!NT_SUCCESS(Status))
150     {
151         DPRINT1("NtCreateKey() failed (Status %lx)\n", Status);
152         return FALSE;
153     }
154 
155     RtlInitUnicodeString(&ValueName,
156                          L"Hotkey");
157 
158     Status = NtSetValueKey(KeyHandle,
159                            &ValueName,
160                            0,
161                            REG_SZ,
162                            (PVOID)Hotkey,
163                            (1 + 1) * sizeof(WCHAR));
164     if (!NT_SUCCESS(Status))
165     {
166         DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
167         NtClose(KeyHandle);
168         return FALSE;
169     }
170 
171     RtlInitUnicodeString(&ValueName,
172                          L"Language Hotkey");
173 
174     Status = NtSetValueKey(KeyHandle,
175                            &ValueName,
176                            0,
177                            REG_SZ,
178                            (PVOID)LangHotkey,
179                            (1 + 1) * sizeof(WCHAR));
180     if (!NT_SUCCESS(Status))
181     {
182         DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
183         NtClose(KeyHandle);
184         return FALSE;
185     }
186 
187     RtlInitUnicodeString(&ValueName,
188                          L"Layout Hotkey");
189 
190     Status = NtSetValueKey(KeyHandle,
191                            &ValueName,
192                            0,
193                            REG_SZ,
194                            (PVOID)LayoutHotkey,
195                            (1 + 1) * sizeof(WCHAR));
196     if (!NT_SUCCESS(Status))
197     {
198         DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
199         NtClose(KeyHandle);
200         return FALSE;
201     }
202 
203     NtClose(KeyHandle);
204     return TRUE;
205 }
206 
207 BOOLEAN
208 AddKbLayoutsToRegistry(
209     _In_ const MUI_LAYOUTS* MuiLayouts)
210 {
211     OBJECT_ATTRIBUTES ObjectAttributes;
212     UNICODE_STRING KeyName;
213     UNICODE_STRING ValueName;
214     HANDLE KeyHandle;
215     HANDLE SubKeyHandle;
216     NTSTATUS Status;
217     ULONG Disposition;
218     ULONG uIndex;
219     ULONG uCount;
220     WCHAR szKeyName[48] = L".DEFAULT\\Keyboard Layout";
221     WCHAR szValueName[3 + 1];
222     WCHAR szSubstID[8 + 1];
223     WCHAR szLayoutID[8 + 1];
224 
225     /* Open the keyboard layout key */
226     RtlInitUnicodeString(&KeyName, szKeyName);
227     InitializeObjectAttributes(&ObjectAttributes,
228                                &KeyName,
229                                OBJ_CASE_INSENSITIVE,
230                                GetRootKeyByPredefKey(HKEY_USERS, NULL),
231                                NULL);
232 
233     Status =  NtCreateKey(&KeyHandle,
234                           KEY_CREATE_SUB_KEY,
235                           &ObjectAttributes,
236                           0,
237                           NULL,
238                           REG_OPTION_NON_VOLATILE,
239                           &Disposition);
240     if (!NT_SUCCESS(Status))
241     {
242         DPRINT1("NtCreateKey() failed (Status %lx)\n", Status);
243         return FALSE;
244     }
245 
246     NtClose(KeyHandle);
247 
248     KeyName.MaximumLength = sizeof(szKeyName);
249     Status = RtlAppendUnicodeToString(&KeyName, L"\\Preload");
250     if (!NT_SUCCESS(Status))
251     {
252         DPRINT1("RtlAppend() failed (%lx), string is '%wZ'\n", Status, &KeyName);
253         return FALSE;
254     }
255 
256     InitializeObjectAttributes(&ObjectAttributes,
257                                &KeyName,
258                                OBJ_CASE_INSENSITIVE,
259                                GetRootKeyByPredefKey(HKEY_USERS, NULL),
260                                NULL);
261 
262     Status = NtCreateKey(&KeyHandle,
263                          KEY_SET_VALUE,
264                          &ObjectAttributes,
265                          0,
266                          NULL,
267                          REG_OPTION_NON_VOLATILE,
268                          &Disposition);
269     if (!NT_SUCCESS(Status))
270     {
271         DPRINT1("NtCreateKey() failed (Status %lx)\n", Status);
272         return FALSE;
273     }
274 
275     RtlInitUnicodeString(&KeyName, L".DEFAULT\\Keyboard Layout\\Substitutes");
276     InitializeObjectAttributes(&ObjectAttributes,
277                                &KeyName,
278                                OBJ_CASE_INSENSITIVE,
279                                GetRootKeyByPredefKey(HKEY_USERS, NULL),
280                                NULL);
281 
282     Status =  NtCreateKey(&SubKeyHandle,
283                           KEY_SET_VALUE,
284                           &ObjectAttributes,
285                           0,
286                           NULL,
287                           REG_OPTION_NON_VOLATILE,
288                           &Disposition);
289     if (!NT_SUCCESS(Status))
290     {
291         DPRINT1("NtCreateKey() failed (Status %lx)\n", Status);
292         goto Quit;
293     }
294 
295     uCount = 0;
296     for (uIndex = 0; (uIndex <= 19) && (MuiLayouts[uIndex].LangID != 0); ++uIndex)
297     {
298         RtlStringCchPrintfW(szValueName, _countof(szValueName), L"%u", uIndex + 1);
299         RtlInitUnicodeString(&ValueName, szValueName);
300 
301         RtlStringCchPrintfW(szLayoutID, _countof(szLayoutID), L"%08lx", MuiLayouts[uIndex].LayoutID);
302 
303         if ((KLID)MuiLayouts[uIndex].LangID == MuiLayouts[uIndex].LayoutID)
304         {
305             /* Main keyboard layout */
306             Status = NtSetValueKey(KeyHandle,
307                                    &ValueName,
308                                    0,
309                                    REG_SZ,
310                                    (PVOID)szLayoutID,
311                                    (wcslen(szLayoutID)+1) * sizeof(WCHAR));
312             if (!NT_SUCCESS(Status))
313             {
314                 DPRINT1("NtSetValueKey() failed (Status = %lx, uIndex = %u)\n", Status, uIndex);
315                 goto Quit;
316             }
317         }
318         else
319         {
320             /* Generate a substitute keyboard layout ID */
321             RtlStringCchPrintfW(szSubstID, _countof(szSubstID), L"%08lx",
322                                 (0xD0000000/*SUBST_MASK*/ | ((USHORT)uCount << 4) | MuiLayouts[uIndex].LangID));
323             Status = NtSetValueKey(KeyHandle,
324                                    &ValueName,
325                                    0,
326                                    REG_SZ,
327                                    (PVOID)szSubstID,
328                                    (wcslen(szSubstID)+1) * sizeof(WCHAR));
329             if (!NT_SUCCESS(Status))
330             {
331                 DPRINT1("NtSetValueKey() failed (Status = %lx, uIndex = %u)\n", Status, uIndex);
332                 goto Quit;
333             }
334 
335             /* Link the substitute layout with the original one */
336             RtlInitUnicodeString(&ValueName, szSubstID);
337             Status = NtSetValueKey(SubKeyHandle,
338                                    &ValueName,
339                                    0,
340                                    REG_SZ,
341                                    (PVOID)szLayoutID,
342                                    (wcslen(szLayoutID)+1) * sizeof(WCHAR));
343             if (!NT_SUCCESS(Status))
344             {
345                 DPRINT1("NtSetValueKey() failed (Status = %lx, uIndex = %u)\n", Status, uIndex);
346                 goto Quit;
347             }
348 
349             ++uCount;
350         }
351     }
352 
353     AddHotkeySettings(L"1", L"1", L"2");
354 
355 Quit:
356     NtClose(SubKeyHandle);
357     NtClose(KeyHandle);
358     return NT_SUCCESS(Status);
359 }
360 
361 BOOLEAN
362 AddKeyboardLayouts(
363     IN PCWSTR LanguageId)
364 {
365     ULONG lngIndex = 0;
366 
367     while (MUILanguageList[lngIndex].LanguageID != NULL)
368     {
369         if (_wcsicmp(MUILanguageList[lngIndex].LanguageID, LanguageId) == 0)
370         {
371             return AddKbLayoutsToRegistry(MUILanguageList[lngIndex].MuiLayouts);
372         }
373 
374         lngIndex++;
375     }
376 
377     return FALSE;
378 }
379 
380 static
381 BOOLEAN
382 AddCodepageToRegistry(
383     _In_ UINT ACPage,
384     _In_ UINT OEMCPage,
385     _In_ UINT MACCPage)
386 {
387     NTSTATUS Status;
388     OBJECT_ATTRIBUTES ObjectAttributes;
389     UNICODE_STRING Name;
390     HANDLE KeyHandle;
391     /*
392      * Buffer big enough to hold the NULL-terminated string L"4294967295",
393      * corresponding to the literal 0xFFFFFFFF (MAXULONG) in decimal.
394      */
395     WCHAR Value[sizeof("4294967295")];
396 
397     /* Open the NLS CodePage key */
398     RtlInitUnicodeString(&Name,
399                          L"SYSTEM\\CurrentControlSet\\Control\\NLS\\CodePage");
400     InitializeObjectAttributes(&ObjectAttributes,
401                                &Name,
402                                OBJ_CASE_INSENSITIVE,
403                                GetRootKeyByPredefKey(HKEY_LOCAL_MACHINE, NULL),
404                                NULL);
405     Status = NtOpenKey(&KeyHandle,
406                        KEY_WRITE,
407                        &ObjectAttributes);
408     if (!NT_SUCCESS(Status))
409     {
410         DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
411         return FALSE;
412     }
413 
414     /* Set ANSI codepage */
415     Status = RtlStringCchPrintfW(Value, _countof(Value), L"%lu", ACPage);
416     ASSERT(NT_SUCCESS(Status));
417 
418     RtlInitUnicodeString(&Name, L"ACP");
419     Status = NtSetValueKey(KeyHandle,
420                            &Name,
421                            0,
422                            REG_SZ,
423                            (PVOID)Value,
424                            (wcslen(Value)+1) * sizeof(WCHAR));
425     if (!NT_SUCCESS(Status))
426     {
427         DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
428         goto Quit;
429     }
430 
431     /* Set OEM codepage */
432     Status = RtlStringCchPrintfW(Value, _countof(Value), L"%lu", OEMCPage);
433     ASSERT(NT_SUCCESS(Status));
434 
435     RtlInitUnicodeString(&Name, L"OEMCP");
436     Status = NtSetValueKey(KeyHandle,
437                            &Name,
438                            0,
439                            REG_SZ,
440                            (PVOID)Value,
441                            (wcslen(Value)+1) * sizeof(WCHAR));
442     if (!NT_SUCCESS(Status))
443     {
444         DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
445         goto Quit;
446     }
447 
448     /* Set MAC codepage */
449     Status = RtlStringCchPrintfW(Value, _countof(Value), L"%lu", MACCPage);
450     ASSERT(NT_SUCCESS(Status));
451 
452     RtlInitUnicodeString(&Name, L"MACCP");
453     Status = NtSetValueKey(KeyHandle,
454                            &Name,
455                            0,
456                            REG_SZ,
457                            (PVOID)Value,
458                            (wcslen(Value)+1) * sizeof(WCHAR));
459     if (!NT_SUCCESS(Status))
460     {
461         DPRINT1("NtSetValueKey() failed (Status %lx)\n", Status);
462         goto Quit;
463     }
464 
465 Quit:
466     NtClose(KeyHandle);
467     return NT_SUCCESS(Status);
468 }
469 
470 static
471 BOOLEAN
472 AddFontsSettingsToRegistry(
473     IN const MUI_SUBFONT * MuiSubFonts)
474 {
475     OBJECT_ATTRIBUTES ObjectAttributes;
476     UNICODE_STRING KeyName;
477     UNICODE_STRING ValueName;
478     HANDLE KeyHandle;
479     NTSTATUS Status;
480     ULONG uIndex = 0;
481 
482     RtlInitUnicodeString(&KeyName,
483                          L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes");
484     InitializeObjectAttributes(&ObjectAttributes,
485                                &KeyName,
486                                OBJ_CASE_INSENSITIVE,
487                                GetRootKeyByPredefKey(HKEY_LOCAL_MACHINE, NULL),
488                                NULL);
489     Status = NtOpenKey(&KeyHandle,
490                        KEY_WRITE,
491                        &ObjectAttributes);
492     if (!NT_SUCCESS(Status))
493     {
494         DPRINT1("NtOpenKey() failed (Status %lx)\n", Status);
495         return FALSE;
496     }
497 
498     while (MuiSubFonts[uIndex].FontName != NULL)
499     {
500         RtlInitUnicodeString(&ValueName, MuiSubFonts[uIndex].FontName);
501         if (MuiSubFonts[uIndex].SubFontName)
502         {
503             Status = NtSetValueKey(KeyHandle,
504                                    &ValueName,
505                                    0,
506                                    REG_SZ,
507                                    (PVOID)MuiSubFonts[uIndex].SubFontName,
508                                    (wcslen(MuiSubFonts[uIndex].SubFontName)+1) * sizeof(WCHAR));
509             if (!NT_SUCCESS(Status))
510             {
511                 DPRINT1("NtSetValueKey() failed (Status = %lx, uIndex = %d)\n", Status, uIndex);
512                 NtClose(KeyHandle);
513                 return FALSE;
514             }
515         }
516         else
517         {
518             Status = NtDeleteValueKey(KeyHandle, &ValueName);
519             if (!NT_SUCCESS(Status))
520             {
521                 DPRINT1("NtDeleteValueKey failed, Status = %lx\n", Status);
522             }
523         }
524 
525         uIndex++;
526     }
527 
528     NtClose(KeyHandle);
529 
530     return TRUE;
531 }
532 
533 BOOLEAN
534 AddCodePage(
535     IN PCWSTR LanguageId)
536 {
537     ULONG lngIndex = 0;
538 
539     while (MUILanguageList[lngIndex].LanguageID != NULL)
540     {
541         if (_wcsicmp(MUILanguageList[lngIndex].LanguageID, LanguageId) == 0)
542         {
543             if (AddCodepageToRegistry(MUILanguageList[lngIndex].ACPage,
544                                       MUILanguageList[lngIndex].OEMCPage,
545                                       MUILanguageList[lngIndex].MACCPage) &&
546                 AddFontsSettingsToRegistry(MUILanguageList[lngIndex].MuiSubFonts))
547             {
548                 return TRUE;
549             }
550             else
551             {
552                 return FALSE;
553             }
554         }
555 
556         lngIndex++;
557     }
558 
559     return FALSE;
560 }
561 
562 #ifdef __REACTOS__ /* HACK */
563 BOOL
564 DoRegistryFontFixup(PFONTSUBSTSETTINGS pSettings, LANGID LangID)
565 {
566     if (pSettings->bFoundFontMINGLIU)
567         AddFontsSettingsToRegistry(FontFixupMINGLIU);
568     if (pSettings->bFoundFontSIMSUN)
569         AddFontsSettingsToRegistry(FontFixupSIMSUN);
570     if (pSettings->bFoundFontMSSONG)
571         AddFontsSettingsToRegistry(FontFixupMSSONG);
572     if (pSettings->bFoundFontMSGOTHIC)
573         AddFontsSettingsToRegistry(FontFixupMSGOTHIC);
574     if (pSettings->bFoundFontMSMINCHO)
575         AddFontsSettingsToRegistry(FontFixupMSMINCHO);
576     if (pSettings->bFoundFontGULIM)
577         AddFontsSettingsToRegistry(FontFixupGULIM);
578     if (pSettings->bFoundFontBATANG)
579         AddFontsSettingsToRegistry(FontFixupBATANG);
580 
581     switch (PRIMARYLANGID(LangID))
582     {
583         case LANG_CHINESE:
584             if (SUBLANGID(LangID) == SUBLANG_CHINESE_SIMPLIFIED)
585             {
586                 if (pSettings->bFoundFontSIMSUN)
587                     AddFontsSettingsToRegistry(SimplifiedChineseFontFixup);
588             }
589             else
590             {
591                 if (pSettings->bFoundFontMINGLIU)
592                     AddFontsSettingsToRegistry(TraditionalChineseFontFixup);
593             }
594             break;
595 
596         case LANG_JAPANESE:
597             if (pSettings->bFoundFontMSGOTHIC)
598                 AddFontsSettingsToRegistry(JapaneseFontFixup);
599             break;
600 
601         case LANG_KOREAN:
602             if (pSettings->bFoundFontBATANG)
603                 AddFontsSettingsToRegistry(KoreanFontFixup);
604             break;
605     }
606 
607     return TRUE;
608 }
609 #endif /* HACK */
610 
611 /* EOF */
612