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