xref: /reactos/dll/win32/kernel32/winnls/string/nls.c (revision 1ac9e484)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * FILE:            dll/win32/kernel32/winnls/string/nls.c
5  * PURPOSE:         National Language Support
6  * PROGRAMMER:      Filip Navara
7  *                  Hartmut Birr
8  *                  Gunnar Andre Dalsnes
9  *                  Thomas Weidenmueller
10  *                  Katayama Hirofumi MZ
11  * UPDATE HISTORY:
12  *                  Created 24/08/2004
13  */
14 
15 /* INCLUDES *******************************************************************/
16 
17 #include <k32.h>
18 
19 #define NDEBUG
20 #include <debug.h>
21 
22 /* GLOBAL VARIABLES ***********************************************************/
23 
24 /* Sequence length based on the first character. */
25 static const char UTF8Length[128] =
26 {
27    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8F */
28    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9F */
29    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xA0 - 0xAF */
30    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xB0 - 0xBF */
31    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xC0 - 0xCF */
32    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0xD0 - 0xDF */
33    2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 0xE0 - 0xEF */
34    3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 0, 0  /* 0xF0 - 0xFF */
35 };
36 
37 /* First byte mask depending on UTF-8 sequence length. */
38 static const unsigned char UTF8Mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
39 
40 /* UTF-8 length to lower bound */
41 static const unsigned long UTF8LBound[] =
42     {0, 0x80, 0x800, 0x10000, 0x200000, 0x2000000, 0xFFFFFFFF};
43 
44 /* FIXME: Change to HASH table or linear array. */
45 static LIST_ENTRY CodePageListHead;
46 static CODEPAGE_ENTRY AnsiCodePage;
47 static CODEPAGE_ENTRY OemCodePage;
48 static RTL_CRITICAL_SECTION CodePageListLock;
49 
50 /* FORWARD DECLARATIONS *******************************************************/
51 
52 BOOL WINAPI
53 GetNlsSectionName(UINT CodePage, UINT Base, ULONG Unknown,
54                   LPSTR BaseName, LPSTR Result, ULONG ResultSize);
55 
56 BOOL WINAPI
57 GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize);
58 
59 /* PRIVATE FUNCTIONS **********************************************************/
60 
61 /**
62  * @name NlsInit
63  *
64  * Internal NLS related stuff initialization.
65  */
66 
67 BOOL
68 FASTCALL
69 NlsInit(VOID)
70 {
71     UNICODE_STRING DirName;
72     OBJECT_ATTRIBUTES ObjectAttributes;
73     HANDLE Handle;
74 
75     InitializeListHead(&CodePageListHead);
76     RtlInitializeCriticalSection(&CodePageListLock);
77 
78     /*
79      * FIXME: Eventually this should be done only for the NLS Server
80      * process, but since we don't have anything like that (yet?) we
81      * always try to create the "\Nls" directory here.
82      */
83     RtlInitUnicodeString(&DirName, L"\\Nls");
84 
85     InitializeObjectAttributes(&ObjectAttributes,
86                                &DirName,
87                                OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
88                                NULL,
89                                NULL);
90 
91     if (NT_SUCCESS(NtCreateDirectoryObject(&Handle, DIRECTORY_ALL_ACCESS, &ObjectAttributes)))
92     {
93         NtClose(Handle);
94     }
95 
96     /* Setup ANSI code page. */
97     AnsiCodePage.SectionHandle = NULL;
98     AnsiCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->AnsiCodePageData;
99 
100     RtlInitCodePageTable((PUSHORT)AnsiCodePage.SectionMapping,
101                          &AnsiCodePage.CodePageTable);
102     AnsiCodePage.CodePage = AnsiCodePage.CodePageTable.CodePage;
103 
104     InsertTailList(&CodePageListHead, &AnsiCodePage.Entry);
105 
106     /* Setup OEM code page. */
107     OemCodePage.SectionHandle = NULL;
108     OemCodePage.SectionMapping = NtCurrentTeb()->ProcessEnvironmentBlock->OemCodePageData;
109 
110     RtlInitCodePageTable((PUSHORT)OemCodePage.SectionMapping,
111                          &OemCodePage.CodePageTable);
112     OemCodePage.CodePage = OemCodePage.CodePageTable.CodePage;
113     InsertTailList(&CodePageListHead, &OemCodePage.Entry);
114 
115     return TRUE;
116 }
117 
118 /**
119  * @name NlsUninit
120  *
121  * Internal NLS related stuff uninitialization.
122  */
123 
124 VOID
125 FASTCALL
126 NlsUninit(VOID)
127 {
128     PCODEPAGE_ENTRY Current;
129 
130     /* Delete the code page list. */
131     while (!IsListEmpty(&CodePageListHead))
132     {
133         Current = CONTAINING_RECORD(CodePageListHead.Flink, CODEPAGE_ENTRY, Entry);
134         if (Current->SectionHandle != NULL)
135         {
136             UnmapViewOfFile(Current->SectionMapping);
137             NtClose(Current->SectionHandle);
138         }
139         RemoveHeadList(&CodePageListHead);
140     }
141     RtlDeleteCriticalSection(&CodePageListLock);
142 }
143 
144 /**
145  * @name IntGetLoadedCodePageEntry
146  *
147  * Internal function to get structure containing a code page information
148  * of code page that is already loaded.
149  *
150  * @param CodePage
151  *        Number of the code page. Special values like CP_OEMCP, CP_ACP
152  *        or CP_UTF8 aren't allowed.
153  *
154  * @return Code page entry or NULL if the specified code page hasn't
155  *         been loaded yet.
156  */
157 
158 PCODEPAGE_ENTRY
159 FASTCALL
160 IntGetLoadedCodePageEntry(UINT CodePage)
161 {
162     LIST_ENTRY *CurrentEntry;
163     PCODEPAGE_ENTRY Current;
164 
165     RtlEnterCriticalSection(&CodePageListLock);
166     for (CurrentEntry = CodePageListHead.Flink;
167          CurrentEntry != &CodePageListHead;
168          CurrentEntry = CurrentEntry->Flink)
169     {
170         Current = CONTAINING_RECORD(CurrentEntry, CODEPAGE_ENTRY, Entry);
171         if (Current->CodePage == CodePage)
172         {
173             RtlLeaveCriticalSection(&CodePageListLock);
174             return Current;
175         }
176     }
177     RtlLeaveCriticalSection(&CodePageListLock);
178 
179     return NULL;
180 }
181 
182 /**
183  * @name IntGetCodePageEntry
184  *
185  * Internal function to get structure containing a code page information.
186  *
187  * @param CodePage
188  *        Number of the code page. Special values like CP_OEMCP, CP_ACP
189  *        or CP_THREAD_ACP are allowed, but CP_UTF[7/8] isn't.
190  *
191  * @return Code page entry.
192  */
193 
194 PCODEPAGE_ENTRY
195 FASTCALL
196 IntGetCodePageEntry(UINT CodePage)
197 {
198     CHAR SectionName[40];
199     NTSTATUS Status;
200     HANDLE SectionHandle = INVALID_HANDLE_VALUE, FileHandle;
201     PBYTE SectionMapping;
202     OBJECT_ATTRIBUTES ObjectAttributes;
203     ANSI_STRING AnsiName;
204     UNICODE_STRING UnicodeName;
205     WCHAR FileName[MAX_PATH + 1];
206     UINT FileNamePos;
207     PCODEPAGE_ENTRY CodePageEntry;
208     if (CodePage == CP_ACP)
209     {
210         return &AnsiCodePage;
211     }
212     else if (CodePage == CP_OEMCP)
213     {
214         return &OemCodePage;
215     }
216     else if (CodePage == CP_THREAD_ACP)
217     {
218         if (!GetLocaleInfoW(GetThreadLocale(),
219                             LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
220                             (WCHAR *)&CodePage,
221                             sizeof(CodePage) / sizeof(WCHAR)))
222         {
223             /* Last error is set by GetLocaleInfoW. */
224             return NULL;
225         }
226         if (CodePage == 0)
227             return &AnsiCodePage;
228     }
229     else if (CodePage == CP_MACCP)
230     {
231         if (!GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT,
232                             LOCALE_IDEFAULTMACCODEPAGE | LOCALE_RETURN_NUMBER,
233                             (WCHAR *)&CodePage,
234                             sizeof(CodePage) / sizeof(WCHAR)))
235         {
236             /* Last error is set by GetLocaleInfoW. */
237             return NULL;
238         }
239     }
240 
241     /* Try searching for loaded page first. */
242     CodePageEntry = IntGetLoadedCodePageEntry(CodePage);
243     if (CodePageEntry != NULL)
244     {
245         return CodePageEntry;
246     }
247 
248     /*
249      * Yes, we really want to lock here. Otherwise it can happen that
250      * two parallel requests will try to get the entry for the same
251      * code page and we would load it twice.
252      */
253     RtlEnterCriticalSection(&CodePageListLock);
254 
255     /* Generate the section name. */
256     if (!GetNlsSectionName(CodePage,
257                            10,
258                            0,
259                            "\\Nls\\NlsSectionCP",
260                            SectionName,
261                            sizeof(SectionName)))
262     {
263         RtlLeaveCriticalSection(&CodePageListLock);
264         return NULL;
265     }
266 
267     RtlInitAnsiString(&AnsiName, SectionName);
268     RtlAnsiStringToUnicodeString(&UnicodeName, &AnsiName, TRUE);
269 
270     InitializeObjectAttributes(&ObjectAttributes, &UnicodeName, 0, NULL, NULL);
271 
272     /* Try to open the section first */
273     Status = NtOpenSection(&SectionHandle, SECTION_MAP_READ, &ObjectAttributes);
274 
275     /* If the section doesn't exist, try to create it. */
276     if (Status == STATUS_UNSUCCESSFUL ||
277         Status == STATUS_OBJECT_NAME_NOT_FOUND ||
278         Status == STATUS_OBJECT_PATH_NOT_FOUND)
279     {
280         FileNamePos = GetSystemDirectoryW(FileName, MAX_PATH);
281         if (GetCPFileNameFromRegistry(CodePage,
282                                       FileName + FileNamePos + 1,
283                                       MAX_PATH - FileNamePos - 1))
284         {
285             FileName[FileNamePos] = L'\\';
286             FileName[MAX_PATH] = 0;
287             FileHandle = CreateFileW(FileName,
288                                      FILE_GENERIC_READ,
289                                      FILE_SHARE_READ,
290                                      NULL,
291                                      OPEN_EXISTING,
292                                      0,
293                                      NULL);
294 
295             Status = NtCreateSection(&SectionHandle,
296                                      SECTION_MAP_READ,
297                                      &ObjectAttributes,
298                                      NULL,
299                                      PAGE_READONLY,
300                                      SEC_COMMIT,
301                                      FileHandle);
302 
303             /* HACK: Check if another process was faster
304              * and already created this section. See bug 3626 for details */
305             if (Status == STATUS_OBJECT_NAME_COLLISION)
306             {
307                 /* Close the file then */
308                 NtClose(FileHandle);
309 
310                 /* And open the section */
311                 Status = NtOpenSection(&SectionHandle,
312                                        SECTION_MAP_READ,
313                                        &ObjectAttributes);
314             }
315         }
316     }
317     RtlFreeUnicodeString(&UnicodeName);
318 
319     if (!NT_SUCCESS(Status))
320     {
321         RtlLeaveCriticalSection(&CodePageListLock);
322         return NULL;
323     }
324 
325     SectionMapping = MapViewOfFile(SectionHandle, FILE_MAP_READ, 0, 0, 0);
326     if (SectionMapping == NULL)
327     {
328         NtClose(SectionHandle);
329         RtlLeaveCriticalSection(&CodePageListLock);
330         return NULL;
331     }
332 
333     CodePageEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CODEPAGE_ENTRY));
334     if (CodePageEntry == NULL)
335     {
336         NtClose(SectionHandle);
337         RtlLeaveCriticalSection(&CodePageListLock);
338         return NULL;
339     }
340 
341     CodePageEntry->CodePage = CodePage;
342     CodePageEntry->SectionHandle = SectionHandle;
343     CodePageEntry->SectionMapping = SectionMapping;
344 
345     RtlInitCodePageTable((PUSHORT)SectionMapping, &CodePageEntry->CodePageTable);
346 
347     /* Insert the new entry to list and unlock. Uff. */
348     InsertTailList(&CodePageListHead, &CodePageEntry->Entry);
349     RtlLeaveCriticalSection(&CodePageListLock);
350 
351     return CodePageEntry;
352 }
353 
354 /**
355  * @name IntMultiByteToWideCharUTF8
356  *
357  * Internal version of MultiByteToWideChar for UTF8.
358  *
359  * @see MultiByteToWideChar
360  */
361 
362 static
363 INT
364 WINAPI
365 IntMultiByteToWideCharUTF8(DWORD Flags,
366                            LPCSTR MultiByteString,
367                            INT MultiByteCount,
368                            LPWSTR WideCharString,
369                            INT WideCharCount)
370 {
371     LPCSTR MbsEnd, MbsPtrSave;
372     UCHAR Char, TrailLength;
373     WCHAR WideChar;
374     LONG Count;
375     BOOL CharIsValid, StringIsValid = TRUE;
376     const WCHAR InvalidChar = 0xFFFD;
377 
378     if (Flags != 0 && Flags != MB_ERR_INVALID_CHARS)
379     {
380         SetLastError(ERROR_INVALID_FLAGS);
381         return 0;
382     }
383 
384     /* Does caller query for output buffer size? */
385     if (WideCharCount == 0)
386     {
387         /* validate and count the wide characters */
388         MbsEnd = MultiByteString + MultiByteCount;
389         for (; MultiByteString < MbsEnd; WideCharCount++)
390         {
391             Char = *MultiByteString++;
392             if (Char < 0xC0)
393             {
394                 TrailLength = 0;
395                 continue;
396             }
397             if (Char >= 0xF8 || (Char & 0xC0) == 0x80)
398             {
399                 TrailLength = 0;
400                 StringIsValid = FALSE;
401                 continue;
402             }
403 
404             CharIsValid = TRUE;
405             MbsPtrSave = MultiByteString;
406             TrailLength = UTF8Length[Char - 0x80];
407             WideChar = Char & UTF8Mask[TrailLength];
408 
409             while (TrailLength && MultiByteString < MbsEnd)
410             {
411                 if ((*MultiByteString & 0xC0) != 0x80)
412                 {
413                     CharIsValid = StringIsValid = FALSE;
414                     break;
415                 }
416 
417                 WideChar = (WideChar << 6) | (*MultiByteString++ & 0x7f);
418                 TrailLength--;
419             }
420 
421             if (!CharIsValid || WideChar < UTF8LBound[UTF8Length[Char - 0x80]])
422             {
423                 MultiByteString = MbsPtrSave;
424             }
425         }
426 
427         if (TrailLength)
428         {
429             WideCharCount++;
430         }
431 
432         if (Flags == MB_ERR_INVALID_CHARS && (!StringIsValid || TrailLength))
433         {
434             SetLastError(ERROR_NO_UNICODE_TRANSLATION);
435             return 0;
436         }
437 
438         return WideCharCount;
439     }
440 
441     /* convert */
442     MbsEnd = MultiByteString + MultiByteCount;
443     for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++)
444     {
445         Char = *MultiByteString++;
446         if (Char < 0x80)
447         {
448             *WideCharString++ = Char;
449             TrailLength = 0;
450             continue;
451         }
452         if (Char >= 0xF8 || Char == 0x80 || (Char & 0xC0) == 0x80)
453         {
454             *WideCharString++ = InvalidChar;
455             TrailLength = 0;
456             continue;
457         }
458 
459         CharIsValid = TRUE;
460         MbsPtrSave = MultiByteString;
461         TrailLength = UTF8Length[Char - 0x80];
462         WideChar = Char & UTF8Mask[TrailLength];
463 
464         while (TrailLength && MultiByteString < MbsEnd)
465         {
466             if ((*MultiByteString & 0xC0) != 0x80)
467             {
468                 CharIsValid = StringIsValid = FALSE;
469                 break;
470             }
471 
472             WideChar = (WideChar << 6) | (*MultiByteString++ & 0x7f);
473             TrailLength--;
474         }
475 
476         if (CharIsValid && UTF8LBound[UTF8Length[Char - 0x80]] <= WideChar)
477         {
478             *WideCharString++ = WideChar;
479         }
480         else
481         {
482             *WideCharString++ = InvalidChar;
483             MultiByteString = MbsPtrSave;
484         }
485     }
486 
487     if (TrailLength && Count < WideCharCount && MultiByteString < MbsEnd)
488     {
489         *WideCharString = InvalidChar;
490         WideCharCount++;
491     }
492 
493     if (MultiByteString < MbsEnd)
494     {
495         SetLastError(ERROR_INSUFFICIENT_BUFFER);
496         return 0;
497     }
498 
499     if (Flags == MB_ERR_INVALID_CHARS && (!StringIsValid || TrailLength))
500     {
501         SetLastError(ERROR_NO_UNICODE_TRANSLATION);
502         return 0;
503     }
504 
505     return Count;
506 }
507 
508 /**
509  * @name IntMultiByteToWideCharCP
510  *
511  * Internal version of MultiByteToWideChar for code page tables.
512  *
513  * @see MultiByteToWideChar
514  * @todo Handle MB_PRECOMPOSED, MB_COMPOSITE, MB_USEGLYPHCHARS and
515  *       DBCS codepages.
516  */
517 
518 static
519 INT
520 WINAPI
521 IntMultiByteToWideCharCP(UINT CodePage,
522                          DWORD Flags,
523                          LPCSTR MultiByteString,
524                          INT MultiByteCount,
525                          LPWSTR WideCharString,
526                          INT WideCharCount)
527 {
528     PCODEPAGE_ENTRY CodePageEntry;
529     PCPTABLEINFO CodePageTable;
530     PUSHORT MultiByteTable;
531     LPCSTR TempString;
532     INT TempLength;
533     USHORT WideChar;
534 
535     /* Get code page table. */
536     CodePageEntry = IntGetCodePageEntry(CodePage);
537     if (CodePageEntry == NULL)
538     {
539         SetLastError(ERROR_INVALID_PARAMETER);
540         return 0;
541     }
542 
543     CodePageTable = &CodePageEntry->CodePageTable;
544 
545     /* If MB_USEGLYPHCHARS flag present and glyph table present */
546     if ((Flags & MB_USEGLYPHCHARS) && CodePageTable->MultiByteTable[256])
547     {
548         /* Use glyph table */
549         MultiByteTable = CodePageTable->MultiByteTable + 256 + 1;
550     }
551     else
552     {
553         MultiByteTable = CodePageTable->MultiByteTable;
554     }
555 
556     /* Different handling for DBCS code pages. */
557     if (CodePageTable->DBCSCodePage)
558     {
559         UCHAR Char;
560         USHORT DBCSOffset;
561         LPCSTR MbsEnd = MultiByteString + MultiByteCount;
562         INT Count;
563 
564         if (Flags & MB_ERR_INVALID_CHARS)
565         {
566             TempString = MultiByteString;
567 
568             while (TempString < MbsEnd)
569             {
570                 DBCSOffset = CodePageTable->DBCSOffsets[(UCHAR)*TempString];
571 
572                 if (DBCSOffset)
573                 {
574                     /* If lead byte is presented, but behind it there is no symbol */
575                     if (((TempString + 1) == MbsEnd) || (*(TempString + 1) == 0))
576                     {
577                         SetLastError(ERROR_NO_UNICODE_TRANSLATION);
578                         return 0;
579                     }
580 
581                     WideChar = CodePageTable->DBCSOffsets[DBCSOffset + *(TempString + 1)];
582 
583                     if (WideChar == CodePageTable->UniDefaultChar &&
584                         MAKEWORD(*(TempString + 1), *TempString) != CodePageTable->TransUniDefaultChar)
585                     {
586                         SetLastError(ERROR_NO_UNICODE_TRANSLATION);
587                         return 0;
588                     }
589 
590                     TempString++;
591                 }
592                 else
593                 {
594                     WideChar = MultiByteTable[(UCHAR)*TempString];
595 
596                     if ((WideChar == CodePageTable->UniDefaultChar &&
597                         *TempString != CodePageTable->TransUniDefaultChar) ||
598                         /* "Private Use" characters */
599                         (WideChar >= 0xE000 && WideChar <= 0xF8FF))
600                     {
601                         SetLastError(ERROR_NO_UNICODE_TRANSLATION);
602                         return 0;
603                     }
604                 }
605 
606                 TempString++;
607             }
608         }
609 
610         /* Does caller query for output buffer size? */
611         if (WideCharCount == 0)
612         {
613             for (; MultiByteString < MbsEnd; WideCharCount++)
614             {
615                 Char = *MultiByteString++;
616 
617                 DBCSOffset = CodePageTable->DBCSOffsets[Char];
618 
619                 if (!DBCSOffset)
620                     continue;
621 
622                 if (MultiByteString < MbsEnd)
623                     MultiByteString++;
624             }
625 
626             return WideCharCount;
627         }
628 
629         for (Count = 0; Count < WideCharCount && MultiByteString < MbsEnd; Count++)
630         {
631             Char = *MultiByteString++;
632 
633             DBCSOffset = CodePageTable->DBCSOffsets[Char];
634 
635             if (!DBCSOffset)
636             {
637                 *WideCharString++ = MultiByteTable[Char];
638                 continue;
639             }
640 
641             if (MultiByteString == MbsEnd)
642             {
643                 *WideCharString++ = MultiByteTable[Char];
644             }
645             else if (*MultiByteString == 0)
646             {
647                 *WideCharString++ = UNICODE_NULL;
648                 MultiByteString++;
649             }
650             else
651             {
652                 *WideCharString++ = CodePageTable->DBCSOffsets[DBCSOffset + (UCHAR)*MultiByteString++];
653             }
654         }
655 
656         if (MultiByteString < MbsEnd)
657         {
658             SetLastError(ERROR_INSUFFICIENT_BUFFER);
659             return 0;
660         }
661 
662         return Count;
663     }
664     else /* SBCS code page */
665     {
666         /* Check for invalid characters. */
667         if (Flags & MB_ERR_INVALID_CHARS)
668         {
669             for (TempString = MultiByteString, TempLength = MultiByteCount;
670                  TempLength > 0;
671                  TempString++, TempLength--)
672             {
673                 WideChar = MultiByteTable[(UCHAR)*TempString];
674 
675                 if ((WideChar == CodePageTable->UniDefaultChar &&
676                     *TempString != CodePageTable->TransUniDefaultChar) ||
677                     /* "Private Use" characters */
678                     (WideChar >= 0xE000 && WideChar <= 0xF8FF))
679                 {
680                     SetLastError(ERROR_NO_UNICODE_TRANSLATION);
681                     return 0;
682                 }
683             }
684         }
685 
686         /* Does caller query for output buffer size? */
687         if (WideCharCount == 0)
688             return MultiByteCount;
689 
690         /* Fill the WideCharString buffer with what will fit: Verified on WinXP */
691         for (TempLength = (WideCharCount < MultiByteCount) ? WideCharCount : MultiByteCount;
692             TempLength > 0;
693             MultiByteString++, TempLength--)
694         {
695             *WideCharString++ = MultiByteTable[(UCHAR)*MultiByteString];
696         }
697 
698         /* Adjust buffer size. Wine trick ;-) */
699         if (WideCharCount < MultiByteCount)
700         {
701             MultiByteCount = WideCharCount;
702             SetLastError(ERROR_INSUFFICIENT_BUFFER);
703             return 0;
704         }
705         return MultiByteCount;
706     }
707 }
708 
709 /**
710  * @name IntMultiByteToWideCharSYMBOL
711  *
712  * Internal version of MultiByteToWideChar for SYMBOL.
713  *
714  * @see MultiByteToWideChar
715  */
716 
717 static
718 INT
719 WINAPI
720 IntMultiByteToWideCharSYMBOL(DWORD Flags,
721                              LPCSTR MultiByteString,
722                              INT MultiByteCount,
723                              LPWSTR WideCharString,
724                              INT WideCharCount)
725 {
726     LONG Count;
727     UCHAR Char;
728     INT WideCharMaxLen;
729 
730 
731     if (Flags != 0)
732     {
733         SetLastError(ERROR_INVALID_FLAGS);
734         return 0;
735     }
736 
737     if (WideCharCount == 0)
738     {
739         return MultiByteCount;
740     }
741 
742     WideCharMaxLen = WideCharCount > MultiByteCount ? MultiByteCount : WideCharCount;
743 
744     for (Count = 0; Count < WideCharMaxLen; Count++)
745     {
746         Char = MultiByteString[Count];
747         if ( Char < 0x20 )
748         {
749             WideCharString[Count] = Char;
750         }
751         else
752         {
753             WideCharString[Count] = Char + 0xf000;
754         }
755     }
756     if (MultiByteCount > WideCharMaxLen)
757     {
758         SetLastError(ERROR_INSUFFICIENT_BUFFER);
759         return 0;
760     }
761 
762     return WideCharMaxLen;
763 }
764 
765 /**
766  * @name IntWideCharToMultiByteSYMBOL
767  *
768  * Internal version of WideCharToMultiByte for SYMBOL.
769  *
770  * @see WideCharToMultiByte
771  */
772 
773 static INT
774 WINAPI
775 IntWideCharToMultiByteSYMBOL(DWORD Flags,
776                              LPCWSTR WideCharString,
777                              INT WideCharCount,
778                              LPSTR MultiByteString,
779                              INT MultiByteCount)
780 {
781     LONG Count;
782     INT MaxLen;
783     WCHAR Char;
784 
785     if (Flags!=0)
786     {
787         SetLastError(ERROR_INVALID_PARAMETER);
788         return 0;
789     }
790 
791 
792     if (MultiByteCount == 0)
793     {
794         return WideCharCount;
795     }
796 
797     MaxLen = MultiByteCount > WideCharCount ? WideCharCount : MultiByteCount;
798     for (Count = 0; Count < MaxLen; Count++)
799     {
800         Char = WideCharString[Count];
801         if (Char < 0x20)
802         {
803             MultiByteString[Count] = (CHAR)Char;
804         }
805         else
806         {
807             if ((Char >= 0xf020) && (Char < 0xf100))
808             {
809                 MultiByteString[Count] = Char - 0xf000;
810             }
811             else
812             {
813                 SetLastError(ERROR_NO_UNICODE_TRANSLATION);
814                 return 0;
815             }
816         }
817     }
818 
819     if (WideCharCount > MaxLen)
820     {
821         SetLastError(ERROR_INSUFFICIENT_BUFFER);
822         return 0;
823     }
824     return MaxLen;
825 }
826 
827 /**
828  * @name IntWideCharToMultiByteUTF8
829  *
830  * Internal version of WideCharToMultiByte for UTF8.
831  *
832  * @see WideCharToMultiByte
833  */
834 
835 static INT
836 WINAPI
837 IntWideCharToMultiByteUTF8(UINT CodePage,
838                            DWORD Flags,
839                            LPCWSTR WideCharString,
840                            INT WideCharCount,
841                            LPSTR MultiByteString,
842                            INT MultiByteCount,
843                            LPCSTR DefaultChar,
844                            LPBOOL UsedDefaultChar)
845 {
846     INT TempLength;
847     DWORD Char;
848 
849     if (Flags)
850     {
851         SetLastError(ERROR_INVALID_FLAGS);
852         return 0;
853     }
854 
855     /* Does caller query for output buffer size? */
856     if (MultiByteCount == 0)
857     {
858         for (TempLength = 0; WideCharCount;
859             WideCharCount--, WideCharString++)
860         {
861             TempLength++;
862             if (*WideCharString >= 0x80)
863             {
864                 TempLength++;
865                 if (*WideCharString >= 0x800)
866                 {
867                     TempLength++;
868                     if (*WideCharString >= 0xd800 && *WideCharString < 0xdc00 &&
869                         WideCharCount >= 1 &&
870                         WideCharString[1] >= 0xdc00 && WideCharString[1] <= 0xe000)
871                     {
872                         WideCharCount--;
873                         WideCharString++;
874                         TempLength++;
875                     }
876                 }
877             }
878         }
879         return TempLength;
880     }
881 
882     for (TempLength = MultiByteCount; WideCharCount; WideCharCount--, WideCharString++)
883     {
884         Char = *WideCharString;
885         if (Char < 0x80)
886         {
887             if (!TempLength)
888             {
889                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
890                 break;
891             }
892             TempLength--;
893             *MultiByteString++ = (CHAR)Char;
894             continue;
895         }
896 
897         if (Char < 0x800)  /* 0x80-0x7ff: 2 bytes */
898         {
899             if (TempLength < 2)
900             {
901                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
902                 break;
903             }
904             MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
905             MultiByteString[0] = 0xc0 | Char;
906             MultiByteString += 2;
907             TempLength -= 2;
908             continue;
909         }
910 
911         /* surrogate pair 0x10000-0x10ffff: 4 bytes */
912         if (Char >= 0xd800 && Char < 0xdc00 &&
913             WideCharCount >= 1 &&
914             WideCharString[1] >= 0xdc00 && WideCharString[1] < 0xe000)
915         {
916             WideCharCount--;
917             WideCharString++;
918 
919             if (TempLength < 4)
920             {
921                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
922                 break;
923             }
924 
925             Char = (Char - 0xd800) << 10;
926             Char |= *WideCharString - 0xdc00;
927             ASSERT(Char <= 0xfffff);
928             Char += 0x10000;
929             ASSERT(Char <= 0x10ffff);
930 
931             MultiByteString[3] = 0x80 | (Char & 0x3f); Char >>= 6;
932             MultiByteString[2] = 0x80 | (Char & 0x3f); Char >>= 6;
933             MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
934             MultiByteString[0] = 0xf0 | Char;
935             MultiByteString += 4;
936             TempLength -= 4;
937             continue;
938         }
939 
940         /* 0x800-0xffff: 3 bytes */
941         if (TempLength < 3)
942         {
943             SetLastError(ERROR_INSUFFICIENT_BUFFER);
944             break;
945         }
946         MultiByteString[2] = 0x80 | (Char & 0x3f); Char >>= 6;
947         MultiByteString[1] = 0x80 | (Char & 0x3f); Char >>= 6;
948         MultiByteString[0] = 0xe0 | Char;
949         MultiByteString += 3;
950         TempLength -= 3;
951     }
952 
953     return MultiByteCount - TempLength;
954 }
955 
956 /**
957  * @name IsValidSBCSMapping
958  *
959  * Checks if ch (single-byte character) is a valid mapping for wch
960  *
961  * @see IntWideCharToMultiByteCP
962  */
963 static
964 inline
965 BOOL
966 IntIsValidSBCSMapping(PCPTABLEINFO CodePageTable, DWORD Flags, WCHAR wch, UCHAR ch)
967 {
968     /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
969     if (Flags & WC_NO_BEST_FIT_CHARS)
970         return (CodePageTable->MultiByteTable[ch] == wch);
971 
972     /* By default, all characters except TransDefaultChar apply as a valid mapping
973        for ch (so also "nearest" characters) */
974     if (ch != CodePageTable->TransDefaultChar)
975         return TRUE;
976 
977     /* The only possible left valid mapping is the default character itself */
978     return (wch == CodePageTable->TransUniDefaultChar);
979 }
980 
981 /**
982  * @name IsValidDBCSMapping
983  *
984  * Checks if ch (double-byte character) is a valid mapping for wch
985  *
986  * @see IntWideCharToMultiByteCP
987  */
988 static inline BOOL
989 IntIsValidDBCSMapping(PCPTABLEINFO CodePageTable, DWORD Flags, WCHAR wch, USHORT ch)
990 {
991     /* If ch is the default character, but the wch is not, it can't be a valid mapping */
992     if (ch == CodePageTable->TransDefaultChar && wch != CodePageTable->TransUniDefaultChar)
993         return FALSE;
994 
995     /* If the WC_NO_BEST_FIT_CHARS flag has been specified, the characters need to match exactly. */
996     if (Flags & WC_NO_BEST_FIT_CHARS)
997     {
998         if(ch & 0xff00)
999         {
1000             USHORT uOffset = CodePageTable->DBCSOffsets[ch >> 8];
1001             /* if (!uOffset) return (CodePageTable->MultiByteTable[ch] == wch); */
1002             return (CodePageTable->DBCSOffsets[uOffset + (ch & 0xff)] == wch);
1003         }
1004 
1005         return (CodePageTable->MultiByteTable[ch] == wch);
1006     }
1007 
1008     /* If we're still here, we have a valid mapping */
1009     return TRUE;
1010 }
1011 
1012 /**
1013  * @name IntWideCharToMultiByteCP
1014  *
1015  * Internal version of WideCharToMultiByte for code page tables.
1016  *
1017  * @see WideCharToMultiByte
1018  * @todo Handle WC_COMPOSITECHECK
1019  */
1020 static
1021 INT
1022 WINAPI
1023 IntWideCharToMultiByteCP(UINT CodePage,
1024                          DWORD Flags,
1025                          LPCWSTR WideCharString,
1026                          INT WideCharCount,
1027                          LPSTR MultiByteString,
1028                          INT MultiByteCount,
1029                          LPCSTR DefaultChar,
1030                          LPBOOL UsedDefaultChar)
1031 {
1032     PCODEPAGE_ENTRY CodePageEntry;
1033     PCPTABLEINFO CodePageTable;
1034     INT TempLength;
1035 
1036     /* Get code page table. */
1037     CodePageEntry = IntGetCodePageEntry(CodePage);
1038     if (CodePageEntry == NULL)
1039     {
1040         SetLastError(ERROR_INVALID_PARAMETER);
1041         return 0;
1042     }
1043 
1044     CodePageTable = &CodePageEntry->CodePageTable;
1045 
1046 
1047     /* Different handling for DBCS code pages. */
1048     if (CodePageTable->DBCSCodePage)
1049     {
1050         /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
1051         if (Flags || DefaultChar || UsedDefaultChar)
1052         {
1053             BOOL TempUsedDefaultChar;
1054             USHORT DefChar;
1055 
1056             /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
1057                to check on every character */
1058             if (!UsedDefaultChar)
1059                 UsedDefaultChar = &TempUsedDefaultChar;
1060 
1061             *UsedDefaultChar = FALSE;
1062 
1063             /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1064             if (DefaultChar)
1065                 DefChar = DefaultChar[1] ? ((DefaultChar[0] << 8) | DefaultChar[1]) : DefaultChar[0];
1066             else
1067                 DefChar = CodePageTable->TransDefaultChar;
1068 
1069             /* Does caller query for output buffer size? */
1070             if (!MultiByteCount)
1071             {
1072                 for (TempLength = 0; WideCharCount; WideCharCount--, WideCharString++, TempLength++)
1073                 {
1074                     USHORT uChar;
1075 
1076                     if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1077                     {
1078                         /* FIXME: Handle WC_COMPOSITECHECK */
1079                         DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1080                     }
1081 
1082                     uChar = ((PUSHORT) CodePageTable->WideCharTable)[*WideCharString];
1083 
1084                     /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
1085                     if (!IntIsValidDBCSMapping(CodePageTable, Flags, *WideCharString, uChar))
1086                     {
1087                         uChar = DefChar;
1088                         *UsedDefaultChar = TRUE;
1089                     }
1090 
1091                     /* Increment TempLength again if this is a double-byte character */
1092                     if (uChar & 0xff00)
1093                         TempLength++;
1094                 }
1095 
1096                 return TempLength;
1097             }
1098 
1099             /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1100             for (TempLength = MultiByteCount;
1101                  WideCharCount && TempLength;
1102                  TempLength--, WideCharString++, WideCharCount--)
1103             {
1104                 USHORT uChar;
1105 
1106                 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1107                 {
1108                     /* FIXME: Handle WC_COMPOSITECHECK */
1109                     DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1110                 }
1111 
1112                 uChar = ((PUSHORT)CodePageTable->WideCharTable)[*WideCharString];
1113 
1114                 /* Verify if the mapping is valid for handling DefaultChar and UsedDefaultChar */
1115                 if (!IntIsValidDBCSMapping(CodePageTable, Flags, *WideCharString, uChar))
1116                 {
1117                     uChar = DefChar;
1118                     *UsedDefaultChar = TRUE;
1119                 }
1120 
1121                 /* Handle double-byte characters */
1122                 if (uChar & 0xff00)
1123                 {
1124                     /* Don't output a partial character */
1125                     if (TempLength == 1)
1126                         break;
1127 
1128                     TempLength--;
1129                     *MultiByteString++ = uChar >> 8;
1130                 }
1131 
1132                 *MultiByteString++ = (char)uChar;
1133             }
1134 
1135             /* WideCharCount should be 0 if all characters were converted */
1136             if (WideCharCount)
1137             {
1138                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1139                 return 0;
1140             }
1141 
1142             return MultiByteCount - TempLength;
1143         }
1144 
1145         /* Does caller query for output buffer size? */
1146         if (!MultiByteCount)
1147         {
1148             for (TempLength = 0; WideCharCount; WideCharCount--, WideCharString++, TempLength++)
1149             {
1150                 /* Increment TempLength again if this is a double-byte character */
1151                 if (((PWCHAR)CodePageTable->WideCharTable)[*WideCharString] & 0xff00)
1152                     TempLength++;
1153             }
1154 
1155             return TempLength;
1156         }
1157 
1158         /* Convert the WideCharString to the MultiByteString */
1159         for (TempLength = MultiByteCount;
1160              WideCharCount && TempLength;
1161              TempLength--, WideCharString++, WideCharCount--)
1162         {
1163             USHORT uChar = ((PUSHORT) CodePageTable->WideCharTable)[*WideCharString];
1164 
1165             /* Is this a double-byte character? */
1166             if (uChar & 0xff00)
1167             {
1168                 /* Don't output a partial character */
1169                 if (TempLength == 1)
1170                     break;
1171 
1172                 TempLength--;
1173                 *MultiByteString++ = uChar >> 8;
1174             }
1175 
1176             *MultiByteString++ = (char)uChar;
1177         }
1178 
1179         /* WideCharCount should be 0 if all characters were converted */
1180         if (WideCharCount)
1181         {
1182             SetLastError(ERROR_INSUFFICIENT_BUFFER);
1183             return 0;
1184         }
1185 
1186         return MultiByteCount - TempLength;
1187     }
1188     else /* SBCS code page */
1189     {
1190         INT nReturn;
1191 
1192         /* If Flags, DefaultChar or UsedDefaultChar were given, we have to do some more work */
1193         if (Flags || DefaultChar || UsedDefaultChar)
1194         {
1195             BOOL TempUsedDefaultChar;
1196             CHAR DefChar;
1197 
1198             /* If UsedDefaultChar is not set, set it to a temporary value, so we don't have
1199                to check on every character */
1200             if (!UsedDefaultChar)
1201                 UsedDefaultChar = &TempUsedDefaultChar;
1202 
1203             *UsedDefaultChar = FALSE;
1204 
1205             /* Does caller query for output buffer size? */
1206             if (!MultiByteCount)
1207             {
1208                 /* Loop through the whole WideCharString and check if we can get a valid mapping for each character */
1209                 for (TempLength = 0; WideCharCount; TempLength++, WideCharString++, WideCharCount--)
1210                 {
1211                     if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1212                     {
1213                         /* FIXME: Handle WC_COMPOSITECHECK */
1214                         DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1215                     }
1216 
1217                     if (!*UsedDefaultChar)
1218                         *UsedDefaultChar = !IntIsValidSBCSMapping(CodePageTable,
1219                                                                   Flags,
1220                                                                   *WideCharString,
1221                                                                   ((PCHAR)CodePageTable->WideCharTable)[*WideCharString]);
1222                 }
1223 
1224                 return TempLength;
1225             }
1226 
1227             /* Use the CodePage's TransDefaultChar if none was given. Don't modify the DefaultChar pointer here. */
1228             if (DefaultChar)
1229                 DefChar = *DefaultChar;
1230             else
1231                 DefChar = (CHAR)CodePageTable->TransDefaultChar;
1232 
1233             /* Convert the WideCharString to the MultiByteString and verify if the mapping is valid */
1234             for (TempLength = MultiByteCount;
1235                  WideCharCount && TempLength;
1236                  MultiByteString++, TempLength--, WideCharString++, WideCharCount--)
1237             {
1238                 if ((Flags & WC_COMPOSITECHECK) && WideCharCount > 1)
1239                 {
1240                     /* FIXME: Handle WC_COMPOSITECHECK */
1241                     DPRINT("WC_COMPOSITECHECK flag UNIMPLEMENTED\n");
1242                 }
1243 
1244                 *MultiByteString = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString];
1245 
1246                 if (!IntIsValidSBCSMapping(CodePageTable, Flags, *WideCharString, *MultiByteString))
1247                 {
1248                     *MultiByteString = DefChar;
1249                     *UsedDefaultChar = TRUE;
1250                 }
1251             }
1252 
1253             /* WideCharCount should be 0 if all characters were converted */
1254             if (WideCharCount)
1255             {
1256                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1257                 return 0;
1258             }
1259 
1260             return MultiByteCount - TempLength;
1261         }
1262 
1263         /* Does caller query for output buffer size? */
1264         if (!MultiByteCount)
1265             return WideCharCount;
1266 
1267         /* Is the buffer large enough? */
1268         if (MultiByteCount < WideCharCount)
1269         {
1270             /* Convert the string up to MultiByteCount and return 0 */
1271             WideCharCount = MultiByteCount;
1272             SetLastError(ERROR_INSUFFICIENT_BUFFER);
1273             nReturn = 0;
1274         }
1275         else
1276         {
1277             /* Otherwise WideCharCount will be the number of converted characters */
1278             nReturn = WideCharCount;
1279         }
1280 
1281         /* Convert the WideCharString to the MultiByteString */
1282         for (TempLength = WideCharCount; --TempLength >= 0; WideCharString++, MultiByteString++)
1283         {
1284             *MultiByteString = ((PCHAR)CodePageTable->WideCharTable)[*WideCharString];
1285         }
1286 
1287         return nReturn;
1288     }
1289 }
1290 
1291 /**
1292  * @name IntIsLeadByte
1293  *
1294  * Internal function to detect if byte is lead byte in specific character
1295  * table.
1296  */
1297 
1298 static BOOL
1299 WINAPI
1300 IntIsLeadByte(PCPTABLEINFO TableInfo, BYTE Byte)
1301 {
1302     UINT i;
1303 
1304     if (TableInfo->MaximumCharacterSize == 2)
1305     {
1306         for (i = 0; i < MAXIMUM_LEADBYTES && TableInfo->LeadByte[i]; i += 2)
1307         {
1308             if (Byte >= TableInfo->LeadByte[i] && Byte <= TableInfo->LeadByte[i+1])
1309                 return TRUE;
1310         }
1311     }
1312 
1313     return FALSE;
1314 }
1315 
1316 /* PUBLIC FUNCTIONS ***********************************************************/
1317 
1318 /**
1319  * @name GetNlsSectionName
1320  *
1321  * Construct a name of NLS section.
1322  *
1323  * @param CodePage
1324  *        Code page number.
1325  * @param Base
1326  *        Integer base used for converting to string. Usually set to 10.
1327  * @param Unknown
1328  *        As the name suggests the meaning of this parameter is unknown.
1329  *        The native version of Kernel32 passes it as the third parameter
1330  *        to NlsConvertIntegerToString function, which is used for the
1331  *        actual conversion of the code page number.
1332  * @param BaseName
1333  *        Base name of the section. (ex. "\\Nls\\NlsSectionCP")
1334  * @param Result
1335  *        Buffer that will hold the constructed name.
1336  * @param ResultSize
1337  *        Size of the buffer for the result.
1338  *
1339  * @return TRUE if the buffer was large enough and was filled with
1340  *         the requested information, FALSE otherwise.
1341  *
1342  * @implemented
1343  */
1344 
1345 BOOL
1346 WINAPI
1347 GetNlsSectionName(UINT CodePage,
1348                   UINT Base,
1349                   ULONG Unknown,
1350                   LPSTR BaseName,
1351                   LPSTR Result,
1352                   ULONG ResultSize)
1353 {
1354     CHAR Integer[11];
1355 
1356     if (!NT_SUCCESS(RtlIntegerToChar(CodePage, Base, sizeof(Integer), Integer)))
1357         return FALSE;
1358 
1359     /*
1360      * If the name including the terminating NULL character doesn't
1361      * fit in the output buffer then fail.
1362      */
1363     if (strlen(Integer) + strlen(BaseName) >= ResultSize)
1364         return FALSE;
1365 
1366     lstrcpyA(Result, BaseName);
1367     lstrcatA(Result, Integer);
1368 
1369     return TRUE;
1370 }
1371 
1372 /**
1373  * @name GetCPFileNameFromRegistry
1374  *
1375  * Get file name of code page definition file.
1376  *
1377  * @param CodePage
1378  *        Code page number to get file name of.
1379  * @param FileName
1380  *        Buffer that is filled with file name of successful return. Can
1381  *        be set to NULL.
1382  * @param FileNameSize
1383  *        Size of the buffer to hold file name in WCHARs.
1384  *
1385  * @return TRUE if the file name was retrieved, FALSE otherwise.
1386  *
1387  * @implemented
1388  */
1389 
1390 BOOL
1391 WINAPI
1392 GetCPFileNameFromRegistry(UINT CodePage, LPWSTR FileName, ULONG FileNameSize)
1393 {
1394     WCHAR ValueNameBuffer[11];
1395     UNICODE_STRING KeyName, ValueName;
1396     OBJECT_ATTRIBUTES ObjectAttributes;
1397     NTSTATUS Status;
1398     HANDLE KeyHandle;
1399     PKEY_VALUE_PARTIAL_INFORMATION Kvpi;
1400     DWORD KvpiSize;
1401     BOOL bRetValue;
1402 
1403     bRetValue = FALSE;
1404 
1405     /* Convert the codepage number to string. */
1406     ValueName.Buffer = ValueNameBuffer;
1407     ValueName.MaximumLength = sizeof(ValueNameBuffer);
1408 
1409     if (!NT_SUCCESS(RtlIntegerToUnicodeString(CodePage, 10, &ValueName)))
1410         return bRetValue;
1411 
1412     /* Open the registry key containing file name mappings. */
1413     RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\System\\"
1414                          L"CurrentControlSet\\Control\\Nls\\CodePage");
1415     InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE,
1416                                NULL, NULL);
1417     Status = NtOpenKey(&KeyHandle, KEY_READ, &ObjectAttributes);
1418     if (!NT_SUCCESS(Status))
1419     {
1420         return bRetValue;
1421     }
1422 
1423     /* Allocate buffer that will be used to query the value data. */
1424     KvpiSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + (MAX_PATH * sizeof(WCHAR));
1425     Kvpi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, KvpiSize);
1426     if (Kvpi == NULL)
1427     {
1428         NtClose(KeyHandle);
1429         return bRetValue;
1430     }
1431 
1432     /* Query the file name for our code page. */
1433     Status = NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation,
1434                              Kvpi, KvpiSize, &KvpiSize);
1435 
1436     NtClose(KeyHandle);
1437 
1438     /* Check if we succeded and the value is non-empty string. */
1439     if (NT_SUCCESS(Status) && Kvpi->Type == REG_SZ &&
1440         Kvpi->DataLength > sizeof(WCHAR))
1441     {
1442         bRetValue = TRUE;
1443         if (FileName != NULL)
1444         {
1445             lstrcpynW(FileName, (WCHAR*)Kvpi->Data,
1446                       min(Kvpi->DataLength / sizeof(WCHAR), FileNameSize));
1447         }
1448     }
1449 
1450     /* free temporary buffer */
1451     HeapFree(GetProcessHeap(),0,Kvpi);
1452     return bRetValue;
1453 }
1454 
1455 /**
1456  * @name IsValidCodePage
1457  *
1458  * Detect if specified code page is valid and present in the system.
1459  *
1460  * @param CodePage
1461  *        Code page number to query.
1462  *
1463  * @return TRUE if code page is present.
1464  */
1465 
1466 BOOL
1467 WINAPI
1468 IsValidCodePage(UINT CodePage)
1469 {
1470     if (CodePage == 0) return FALSE;
1471     if (CodePage == CP_UTF8 || CodePage == CP_UTF7)
1472         return TRUE;
1473     if (IntGetLoadedCodePageEntry(CodePage))
1474         return TRUE;
1475     return GetCPFileNameFromRegistry(CodePage, NULL, 0);
1476 }
1477 
1478 static inline BOOL utf7_write_w(WCHAR *dst, int dstlen, int *index, WCHAR character)
1479 {
1480     if (dstlen > 0)
1481     {
1482         if (*index >= dstlen)
1483             return FALSE;
1484 
1485         dst[*index] = character;
1486     }
1487 
1488     (*index)++;
1489 
1490     return TRUE;
1491 }
1492 
1493 static INT Utf7ToWideChar(const char *src, int srclen, WCHAR *dst, int dstlen)
1494 {
1495     static const signed char base64_decoding_table[] =
1496     {
1497         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00-0x0F */
1498         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10-0x1F */
1499         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20-0x2F */
1500         52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30-0x3F */
1501         -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, /* 0x40-0x4F */
1502         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50-0x5F */
1503         -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60-0x6F */
1504         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1  /* 0x70-0x7F */
1505     };
1506 
1507     const char *source_end = src + srclen;
1508     int dest_index = 0;
1509 
1510     DWORD byte_pair = 0;
1511     short offset = 0;
1512 
1513     while (src < source_end)
1514     {
1515         if (*src == '+')
1516         {
1517             src++;
1518             if (src >= source_end)
1519                 break;
1520 
1521             if (*src == '-')
1522             {
1523                 /* just a plus sign escaped as +- */
1524                 if (!utf7_write_w(dst, dstlen, &dest_index, '+'))
1525                 {
1526                     SetLastError(ERROR_INSUFFICIENT_BUFFER);
1527                     return 0;
1528                 }
1529                 src++;
1530                 continue;
1531             }
1532 
1533             do
1534             {
1535                 signed char sextet = *src;
1536                 if (sextet == '-')
1537                 {
1538                     /* skip over the dash and end base64 decoding
1539                      * the current, unfinished byte pair is discarded */
1540                     src++;
1541                     offset = 0;
1542                     break;
1543                 }
1544                 if (sextet < 0)
1545                 {
1546                     /* the next character of src is < 0 and therefore not part of a base64 sequence
1547                      * the current, unfinished byte pair is NOT discarded in this case
1548                      * this is probably a bug in Windows */
1549                     break;
1550                 }
1551 
1552                 sextet = base64_decoding_table[sextet];
1553                 if (sextet == -1)
1554                 {
1555                     /* -1 means that the next character of src is not part of a base64 sequence
1556                      * in other words, all sextets in this base64 sequence have been processed
1557                      * the current, unfinished byte pair is discarded */
1558                     offset = 0;
1559                     break;
1560                 }
1561 
1562                 byte_pair = (byte_pair << 6) | sextet;
1563                 offset += 6;
1564 
1565                 if (offset >= 16)
1566                 {
1567                     /* this byte pair is done */
1568                     if (!utf7_write_w(dst, dstlen, &dest_index, (byte_pair >> (offset - 16)) & 0xFFFF))
1569                     {
1570                         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1571                         return 0;
1572                     }
1573                     offset -= 16;
1574                 }
1575 
1576                 src++;
1577             }
1578             while (src < source_end);
1579         }
1580         else
1581         {
1582             /* we have to convert to unsigned char in case *src < 0 */
1583             if (!utf7_write_w(dst, dstlen, &dest_index, (unsigned char)*src))
1584             {
1585                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1586                 return 0;
1587             }
1588             src++;
1589         }
1590     }
1591 
1592     return dest_index;
1593 }
1594 
1595 /**
1596  * @name MultiByteToWideChar
1597  *
1598  * Convert a multi-byte string to wide-charater equivalent.
1599  *
1600  * @param CodePage
1601  *        Code page to be used to perform the conversion. It can be also
1602  *        one of the special values (CP_ACP for ANSI code page, CP_MACCP
1603  *        for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
1604  *        for thread active code page, CP_UTF7 or CP_UTF8).
1605  * @param Flags
1606  *        Additional conversion flags (MB_PRECOMPOSED, MB_COMPOSITE,
1607  *        MB_ERR_INVALID_CHARS, MB_USEGLYPHCHARS).
1608  * @param MultiByteString
1609  *        Input buffer.
1610  * @param MultiByteCount
1611  *        Size of MultiByteString, or -1 if MultiByteString is NULL
1612  *        terminated.
1613  * @param WideCharString
1614  *        Output buffer.
1615  * @param WideCharCount
1616  *        Size in WCHARs of WideCharString, or 0 if the caller just wants
1617  *        to know how large WideCharString should be for a successful
1618  *        conversion.
1619  *
1620  * @return Zero on error, otherwise the number of WCHARs written
1621  *         in the WideCharString buffer.
1622  *
1623  * @implemented
1624  */
1625 
1626 INT
1627 WINAPI
1628 MultiByteToWideChar(UINT CodePage,
1629                     DWORD Flags,
1630                     LPCSTR MultiByteString,
1631                     INT MultiByteCount,
1632                     LPWSTR WideCharString,
1633                     INT WideCharCount)
1634 {
1635     /* Check the parameters. */
1636     if (MultiByteString == NULL ||
1637         MultiByteCount == 0 || WideCharCount < 0 ||
1638         (WideCharCount && (WideCharString == NULL ||
1639         (PVOID)MultiByteString == (PVOID)WideCharString)))
1640     {
1641         SetLastError(ERROR_INVALID_PARAMETER);
1642         return 0;
1643     }
1644 
1645     /* Determine the input string length. */
1646     if (MultiByteCount < 0)
1647     {
1648         MultiByteCount = lstrlenA(MultiByteString) + 1;
1649     }
1650 
1651     switch (CodePage)
1652     {
1653         case CP_UTF8:
1654             return IntMultiByteToWideCharUTF8(Flags,
1655                                               MultiByteString,
1656                                               MultiByteCount,
1657                                               WideCharString,
1658                                               WideCharCount);
1659 
1660         case CP_UTF7:
1661             if (Flags)
1662             {
1663                 SetLastError(ERROR_INVALID_FLAGS);
1664                 return 0;
1665             }
1666             return Utf7ToWideChar(MultiByteString, MultiByteCount,
1667                                   WideCharString, WideCharCount);
1668 
1669         case CP_SYMBOL:
1670             return IntMultiByteToWideCharSYMBOL(Flags,
1671                                                 MultiByteString,
1672                                                 MultiByteCount,
1673                                                 WideCharString,
1674                                                 WideCharCount);
1675         default:
1676             return IntMultiByteToWideCharCP(CodePage,
1677                                             Flags,
1678                                             MultiByteString,
1679                                             MultiByteCount,
1680                                             WideCharString,
1681                                             WideCharCount);
1682     }
1683 }
1684 
1685 static inline BOOL utf7_can_directly_encode(WCHAR codepoint)
1686 {
1687     static const BOOL directly_encodable_table[] =
1688     {
1689         1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0x00 - 0x0F */
1690         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1F */
1691         1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, /* 0x20 - 0x2F */
1692         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x30 - 0x3F */
1693         0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 - 0x4F */
1694         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50 - 0x5F */
1695         0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 - 0x6F */
1696         1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1                 /* 0x70 - 0x7A */
1697     };
1698 
1699     return codepoint <= 0x7A ? directly_encodable_table[codepoint] : FALSE;
1700 }
1701 
1702 static inline BOOL utf7_write_c(char *dst, int dstlen, int *index, char character)
1703 {
1704     if (dstlen > 0)
1705     {
1706         if (*index >= dstlen)
1707             return FALSE;
1708 
1709         dst[*index] = character;
1710     }
1711 
1712     (*index)++;
1713 
1714     return TRUE;
1715 }
1716 
1717 static INT WideCharToUtf7(const WCHAR *src, int srclen, char *dst, int dstlen)
1718 {
1719     static const char base64_encoding_table[] =
1720         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1721 
1722     const WCHAR *source_end = src + srclen;
1723     int dest_index = 0;
1724 
1725     while (src < source_end)
1726     {
1727         if (*src == '+')
1728         {
1729             if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
1730             {
1731                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1732                 return 0;
1733             }
1734             if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
1735             {
1736                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1737                 return 0;
1738             }
1739             src++;
1740         }
1741         else if (utf7_can_directly_encode(*src))
1742         {
1743             if (!utf7_write_c(dst, dstlen, &dest_index, *src))
1744             {
1745                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1746                 return 0;
1747             }
1748             src++;
1749         }
1750         else
1751         {
1752             unsigned int offset = 0;
1753             DWORD byte_pair = 0;
1754 
1755             if (!utf7_write_c(dst, dstlen, &dest_index, '+'))
1756             {
1757                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1758                 return 0;
1759             }
1760 
1761             while (src < source_end && !utf7_can_directly_encode(*src))
1762             {
1763                 byte_pair = (byte_pair << 16) | *src;
1764                 offset += 16;
1765                 while (offset >= 6)
1766                 {
1767                     if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[(byte_pair >> (offset - 6)) & 0x3F]))
1768                     {
1769                         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1770                         return 0;
1771                     }
1772                     offset -= 6;
1773                 }
1774                 src++;
1775             }
1776 
1777             if (offset)
1778             {
1779                 /* Windows won't create a padded base64 character if there's no room for the - sign
1780                  * as well ; this is probably a bug in Windows */
1781                 if (dstlen > 0 && dest_index + 1 >= dstlen)
1782                 {
1783                     SetLastError(ERROR_INSUFFICIENT_BUFFER);
1784                     return 0;
1785                 }
1786 
1787                 byte_pair <<= (6 - offset);
1788                 if (!utf7_write_c(dst, dstlen, &dest_index, base64_encoding_table[byte_pair & 0x3F]))
1789                 {
1790                     SetLastError(ERROR_INSUFFICIENT_BUFFER);
1791                     return 0;
1792                 }
1793             }
1794 
1795             /* Windows always explicitly terminates the base64 sequence
1796                even though RFC 2152 (page 3, rule 2) does not require this */
1797             if (!utf7_write_c(dst, dstlen, &dest_index, '-'))
1798             {
1799                 SetLastError(ERROR_INSUFFICIENT_BUFFER);
1800                 return 0;
1801             }
1802         }
1803     }
1804 
1805     return dest_index;
1806 }
1807 
1808 /*
1809  * A function similar to LoadStringW, but adapted for usage by GetCPInfoExW
1810  * and GetGeoInfoW. It uses the current user localization, otherwise falls back
1811  * to English (US). Contrary to LoadStringW which always saves the loaded string
1812  * into the user-given buffer, truncating the string if needed, this function
1813  * returns instead an ERROR_INSUFFICIENT_BUFFER error code if the user buffer
1814  * is not large enough.
1815  */
1816 UINT
1817 GetLocalisedText(
1818     IN UINT uID,
1819     IN LPWSTR lpszDest,
1820     IN UINT cchDest)
1821 {
1822     HRSRC hrsrc;
1823     HGLOBAL hmem;
1824     LCID lcid;
1825     LANGID langId;
1826     const WCHAR *p;
1827     UINT i;
1828 
1829     /* See HACK in winnls/lang/xx-XX.rc files */
1830     if (uID == 37)
1831         uID = uID * 100;
1832 
1833     lcid = GetUserDefaultLCID();
1834     lcid = ConvertDefaultLocale(lcid);
1835 
1836     langId = LANGIDFROMLCID(lcid);
1837 
1838     if (PRIMARYLANGID(langId) == LANG_NEUTRAL)
1839         langId = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
1840 
1841     hrsrc = FindResourceExW(hCurrentModule,
1842                             (LPWSTR)RT_STRING,
1843                             MAKEINTRESOURCEW((uID >> 4) + 1),
1844                             langId);
1845 
1846     /* English fallback */
1847     if (!hrsrc)
1848     {
1849         hrsrc = FindResourceExW(hCurrentModule,
1850                                 (LPWSTR)RT_STRING,
1851                                 MAKEINTRESOURCEW((uID >> 4) + 1),
1852                                 MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
1853     }
1854 
1855     if (!hrsrc)
1856         goto NotFound;
1857 
1858     hmem = LoadResource(hCurrentModule, hrsrc);
1859     if (!hmem)
1860         goto NotFound;
1861 
1862     p = LockResource(hmem);
1863 
1864     for (i = 0; i < (uID & 0x0F); i++)
1865         p += *p + 1;
1866 
1867     /* Needed for GetGeoInfo(): return the needed string size including the NULL terminator */
1868     if (cchDest == 0)
1869         return *p + 1;
1870     /* Needed for GetGeoInfo(): bail out if the user buffer is not large enough */
1871     if (*p + 1 > cchDest)
1872     {
1873         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1874         return 0;
1875     }
1876 
1877     i = *p;
1878     if (i > 0)
1879     {
1880         memcpy(lpszDest, p + 1, i * sizeof(WCHAR));
1881         lpszDest[i] = L'\0';
1882         return i;
1883     }
1884 #if 0
1885     else
1886     {
1887         if (cchDest >= 1)
1888             lpszDest[0] = L'\0';
1889         /* Fall-back */
1890     }
1891 #endif
1892 
1893 NotFound:
1894     DPRINT1("Resource not found: uID = %lu\n", uID);
1895     SetLastError(ERROR_INVALID_PARAMETER);
1896     return 0;
1897 }
1898 
1899 /*
1900  * @implemented
1901  */
1902 BOOL
1903 WINAPI
1904 GetCPInfo(UINT CodePage,
1905           LPCPINFO CodePageInfo)
1906 {
1907     PCODEPAGE_ENTRY CodePageEntry;
1908 
1909     if (!CodePageInfo)
1910     {
1911         SetLastError(ERROR_INVALID_PARAMETER);
1912         return FALSE;
1913     }
1914 
1915     CodePageEntry = IntGetCodePageEntry(CodePage);
1916     if (CodePageEntry == NULL)
1917     {
1918         switch(CodePage)
1919         {
1920             case CP_UTF7:
1921             case CP_UTF8:
1922                 CodePageInfo->DefaultChar[0] = 0x3f;
1923                 CodePageInfo->DefaultChar[1] = 0;
1924                 CodePageInfo->LeadByte[0] = CodePageInfo->LeadByte[1] = 0;
1925                 CodePageInfo->MaxCharSize = (CodePage == CP_UTF7) ? 5 : 4;
1926                 return TRUE;
1927         }
1928 
1929         DPRINT1("Invalid CP!: %lx\n", CodePage);
1930         SetLastError( ERROR_INVALID_PARAMETER );
1931         return FALSE;
1932     }
1933 
1934     if (CodePageEntry->CodePageTable.DefaultChar & 0xff00)
1935     {
1936         CodePageInfo->DefaultChar[0] = (CodePageEntry->CodePageTable.DefaultChar & 0xff00) >> 8;
1937         CodePageInfo->DefaultChar[1] = CodePageEntry->CodePageTable.DefaultChar & 0x00ff;
1938     }
1939     else
1940     {
1941         CodePageInfo->DefaultChar[0] = CodePageEntry->CodePageTable.DefaultChar & 0xff;
1942         CodePageInfo->DefaultChar[1] = 0;
1943     }
1944 
1945     if ((CodePageInfo->MaxCharSize = CodePageEntry->CodePageTable.MaximumCharacterSize) == 2)
1946         memcpy(CodePageInfo->LeadByte, CodePageEntry->CodePageTable.LeadByte, sizeof(CodePageInfo->LeadByte));
1947     else
1948         CodePageInfo->LeadByte[0] = CodePageInfo->LeadByte[1] = 0;
1949 
1950     return TRUE;
1951 }
1952 
1953 /*
1954  * @implemented
1955  */
1956 BOOL
1957 WINAPI
1958 GetCPInfoExW(UINT CodePage,
1959              DWORD dwFlags,
1960              LPCPINFOEXW lpCPInfoEx)
1961 {
1962     if (!GetCPInfo(CodePage, (LPCPINFO)lpCPInfoEx))
1963         return FALSE;
1964 
1965     switch(CodePage)
1966     {
1967         case CP_UTF7:
1968         {
1969             lpCPInfoEx->CodePage = CP_UTF7;
1970             lpCPInfoEx->UnicodeDefaultChar = 0x3f;
1971             return GetLocalisedText(lpCPInfoEx->CodePage,
1972                                     lpCPInfoEx->CodePageName,
1973                                     ARRAYSIZE(lpCPInfoEx->CodePageName)) != 0;
1974         }
1975         break;
1976 
1977         case CP_UTF8:
1978         {
1979             lpCPInfoEx->CodePage = CP_UTF8;
1980             lpCPInfoEx->UnicodeDefaultChar = 0x3f;
1981             return GetLocalisedText(lpCPInfoEx->CodePage,
1982                                     lpCPInfoEx->CodePageName,
1983                                     ARRAYSIZE(lpCPInfoEx->CodePageName)) != 0;
1984         }
1985 
1986         default:
1987         {
1988             PCODEPAGE_ENTRY CodePageEntry;
1989 
1990             CodePageEntry = IntGetCodePageEntry(CodePage);
1991             if (CodePageEntry == NULL)
1992             {
1993                 DPRINT1("Could not get CodePage Entry! CodePageEntry = NULL\n");
1994                 SetLastError(ERROR_INVALID_PARAMETER);
1995                 return FALSE;
1996             }
1997 
1998             lpCPInfoEx->CodePage = CodePageEntry->CodePageTable.CodePage;
1999             lpCPInfoEx->UnicodeDefaultChar = CodePageEntry->CodePageTable.UniDefaultChar;
2000             return GetLocalisedText(lpCPInfoEx->CodePage,
2001                                     lpCPInfoEx->CodePageName,
2002                                     ARRAYSIZE(lpCPInfoEx->CodePageName)) != 0;
2003         }
2004         break;
2005     }
2006 }
2007 
2008 
2009 /*
2010  * @implemented
2011  */
2012 BOOL
2013 WINAPI
2014 GetCPInfoExA(UINT CodePage,
2015              DWORD dwFlags,
2016              LPCPINFOEXA lpCPInfoEx)
2017 {
2018     CPINFOEXW CPInfo;
2019 
2020     if (!GetCPInfoExW(CodePage, dwFlags, &CPInfo))
2021         return FALSE;
2022 
2023     /* the layout is the same except for CodePageName */
2024     memcpy(lpCPInfoEx, &CPInfo, sizeof(CPINFOEXA));
2025 
2026     WideCharToMultiByte(CP_ACP,
2027                         0,
2028                         CPInfo.CodePageName,
2029                         -1,
2030                         lpCPInfoEx->CodePageName,
2031                         sizeof(lpCPInfoEx->CodePageName),
2032                         NULL,
2033                         NULL);
2034     return TRUE;
2035 }
2036 
2037 /**
2038  * @name WideCharToMultiByte
2039  *
2040  * Convert a wide-charater string to closest multi-byte equivalent.
2041  *
2042  * @param CodePage
2043  *        Code page to be used to perform the conversion. It can be also
2044  *        one of the special values (CP_ACP for ANSI code page, CP_MACCP
2045  *        for Macintosh code page, CP_OEMCP for OEM code page, CP_THREAD_ACP
2046  *        for thread active code page, CP_UTF7 or CP_UTF8).
2047  * @param Flags
2048  *        Additional conversion flags (WC_NO_BEST_FIT_CHARS, WC_COMPOSITECHECK,
2049  *        WC_DISCARDNS, WC_SEPCHARS, WC_DEFAULTCHAR).
2050  * @param WideCharString
2051  *        Points to the wide-character string to be converted.
2052  * @param WideCharCount
2053  *        Size in WCHARs of WideCharStr, or 0 if the caller just wants to
2054  *        know how large WideCharString should be for a successful conversion.
2055  * @param MultiByteString
2056  *        Points to the buffer to receive the translated string.
2057  * @param MultiByteCount
2058  *        Specifies the size in bytes of the buffer pointed to by the
2059  *        MultiByteString parameter. If this value is zero, the function
2060  *        returns the number of bytes required for the buffer.
2061  * @param DefaultChar
2062  *        Points to the character used if a wide character cannot be
2063  *        represented in the specified code page. If this parameter is
2064  *        NULL, a system default value is used.
2065  * @param UsedDefaultChar
2066  *        Points to a flag that indicates whether a default character was
2067  *        used. This parameter can be NULL.
2068  *
2069  * @return Zero on error, otherwise the number of bytes written in the
2070  *         MultiByteString buffer. Or the number of bytes needed for
2071  *         the MultiByteString buffer if MultiByteCount is zero.
2072  *
2073  * @implemented
2074  */
2075 
2076 INT
2077 WINAPI
2078 WideCharToMultiByte(UINT CodePage,
2079                     DWORD Flags,
2080                     LPCWSTR WideCharString,
2081                     INT WideCharCount,
2082                     LPSTR MultiByteString,
2083                     INT MultiByteCount,
2084                     LPCSTR DefaultChar,
2085                     LPBOOL UsedDefaultChar)
2086 {
2087     /* Check the parameters. */
2088     if (WideCharString == NULL ||
2089         WideCharCount == 0 ||
2090         (MultiByteString == NULL && MultiByteCount > 0) ||
2091         (PVOID)WideCharString == (PVOID)MultiByteString ||
2092         MultiByteCount < 0)
2093     {
2094         SetLastError(ERROR_INVALID_PARAMETER);
2095         return 0;
2096     }
2097 
2098     /* Determine the input string length. */
2099     if (WideCharCount < 0)
2100     {
2101         WideCharCount = lstrlenW(WideCharString) + 1;
2102     }
2103 
2104     switch (CodePage)
2105     {
2106         case CP_UTF8:
2107             if (DefaultChar != NULL || UsedDefaultChar != NULL)
2108             {
2109                 SetLastError(ERROR_INVALID_PARAMETER);
2110                 return 0;
2111             }
2112             return IntWideCharToMultiByteUTF8(CodePage,
2113                                               Flags,
2114                                               WideCharString,
2115                                               WideCharCount,
2116                                               MultiByteString,
2117                                               MultiByteCount,
2118                                               DefaultChar,
2119                                               UsedDefaultChar);
2120 
2121         case CP_UTF7:
2122             if (DefaultChar != NULL || UsedDefaultChar != NULL)
2123             {
2124                 SetLastError(ERROR_INVALID_PARAMETER);
2125                 return 0;
2126             }
2127             if (Flags)
2128             {
2129                 SetLastError(ERROR_INVALID_FLAGS);
2130                 return 0;
2131             }
2132             return WideCharToUtf7(WideCharString, WideCharCount,
2133                                   MultiByteString, MultiByteCount);
2134 
2135         case CP_SYMBOL:
2136             if ((DefaultChar!=NULL) || (UsedDefaultChar!=NULL))
2137             {
2138                 SetLastError(ERROR_INVALID_PARAMETER);
2139                 return 0;
2140             }
2141             return IntWideCharToMultiByteSYMBOL(Flags,
2142                                                 WideCharString,
2143                                                 WideCharCount,
2144                                                 MultiByteString,
2145                                                 MultiByteCount);
2146 
2147         default:
2148             return IntWideCharToMultiByteCP(CodePage,
2149                                             Flags,
2150                                             WideCharString,
2151                                             WideCharCount,
2152                                             MultiByteString,
2153                                             MultiByteCount,
2154                                             DefaultChar,
2155                                             UsedDefaultChar);
2156    }
2157 }
2158 
2159 /**
2160  * @name GetACP
2161  *
2162  * Get active ANSI code page number.
2163  *
2164  * @implemented
2165  */
2166 
2167 UINT
2168 WINAPI
2169 GetACP(VOID)
2170 {
2171     return AnsiCodePage.CodePageTable.CodePage;
2172 }
2173 
2174 /**
2175  * @name GetOEMCP
2176  *
2177  * Get active OEM code page number.
2178  *
2179  * @implemented
2180  */
2181 
2182 UINT
2183 WINAPI
2184 GetOEMCP(VOID)
2185 {
2186     return OemCodePage.CodePageTable.CodePage;
2187 }
2188 
2189 /**
2190  * @name IsDBCSLeadByteEx
2191  *
2192  * Determine if passed byte is lead byte in specified code page.
2193  *
2194  * @implemented
2195  */
2196 
2197 BOOL
2198 WINAPI
2199 IsDBCSLeadByteEx(UINT CodePage, BYTE TestByte)
2200 {
2201     PCODEPAGE_ENTRY CodePageEntry;
2202 
2203     CodePageEntry = IntGetCodePageEntry(CodePage);
2204     if (CodePageEntry != NULL)
2205         return IntIsLeadByte(&CodePageEntry->CodePageTable, TestByte);
2206 
2207     SetLastError(ERROR_INVALID_PARAMETER);
2208     return FALSE;
2209 }
2210 
2211 /**
2212  * @name IsDBCSLeadByteEx
2213  *
2214  * Determine if passed byte is lead byte in current ANSI code page.
2215  *
2216  * @implemented
2217  */
2218 
2219 BOOL
2220 WINAPI
2221 IsDBCSLeadByte(BYTE TestByte)
2222 {
2223     return IntIsLeadByte(&AnsiCodePage.CodePageTable, TestByte);
2224 }
2225 
2226 /*
2227  * @unimplemented
2228  */
2229 NTSTATUS WINAPI CreateNlsSecurityDescriptor(PSECURITY_DESCRIPTOR SecurityDescriptor,ULONG Size,ULONG AccessMask)
2230 {
2231     STUB;
2232     return 0;
2233 }
2234 
2235 /*
2236  * @unimplemented
2237  */
2238 BOOL WINAPI IsValidUILanguage(LANGID langid)
2239 {
2240     STUB;
2241     return 0;
2242 }
2243 
2244 /*
2245  * @unimplemented
2246  */
2247 VOID WINAPI NlsConvertIntegerToString(ULONG Value,ULONG Base,ULONG strsize, LPWSTR str, ULONG strsize2)
2248 {
2249     STUB;
2250 }
2251 
2252 /*
2253  * @unimplemented
2254  */
2255 UINT WINAPI SetCPGlobal(UINT CodePage)
2256 {
2257     STUB;
2258     return 0;
2259 }
2260 
2261 /*
2262  * @unimplemented
2263  */
2264 BOOL
2265 WINAPI
2266 ValidateLCType(int a1, unsigned int a2, int a3, int a4)
2267 {
2268     STUB;
2269     return FALSE;
2270 }
2271 
2272 /*
2273  * @unimplemented
2274  */
2275 BOOL
2276 WINAPI
2277 NlsResetProcessLocale(VOID)
2278 {
2279     STUB;
2280     return TRUE;
2281 }
2282 
2283 /*
2284  * @unimplemented
2285  */
2286 VOID
2287 WINAPI
2288 GetDefaultSortkeySize(LPVOID lpUnknown)
2289 {
2290     STUB;
2291     lpUnknown = NULL;
2292 }
2293 
2294 /*
2295  * @unimplemented
2296  */
2297 VOID
2298 WINAPI
2299 GetLinguistLangSize(LPVOID lpUnknown)
2300 {
2301     STUB;
2302     lpUnknown = NULL;
2303 }
2304 
2305 /*
2306  * @unimplemented
2307  */
2308 BOOL
2309 WINAPI
2310 ValidateLocale(IN ULONG LocaleId)
2311 {
2312     STUB;
2313     return TRUE;
2314 }
2315 
2316 /*
2317  * @unimplemented
2318  */
2319 ULONG
2320 WINAPI
2321 NlsGetCacheUpdateCount(VOID)
2322 {
2323     STUB;
2324     return 0;
2325 }
2326 
2327 /*
2328  * @unimplemented
2329  */
2330 BOOL
2331 WINAPI
2332 IsNLSDefinedString(IN NLS_FUNCTION Function,
2333                    IN DWORD dwFlags,
2334                    IN LPNLSVERSIONINFO lpVersionInformation,
2335                    IN LPCWSTR lpString,
2336                    IN INT cchStr)
2337 {
2338     STUB;
2339     return TRUE;
2340 }
2341 
2342 /*
2343  * @unimplemented
2344  */
2345 BOOL
2346 WINAPI
2347 GetNLSVersion(IN NLS_FUNCTION Function,
2348               IN LCID Locale,
2349               IN OUT LPNLSVERSIONINFO lpVersionInformation)
2350 {
2351     STUB;
2352     return TRUE;
2353 }
2354 
2355 /*
2356  * @unimplemented
2357  */
2358 BOOL
2359 WINAPI
2360 GetNLSVersionEx(IN NLS_FUNCTION function,
2361                 IN LPCWSTR lpLocaleName,
2362                 IN OUT LPNLSVERSIONINFOEX lpVersionInformation)
2363 {
2364     STUB;
2365     return TRUE;
2366 }
2367 
2368 /* EOF */
2369