1*c2c66affSColin Finck /*
2*c2c66affSColin Finck  * PROJECT:     ReactOS NLS to TXT Converter
3*c2c66affSColin Finck  * LICENSE:     GNU General Public License Version 2.0 or any later version
4*c2c66affSColin Finck  * FILE:        devutils/nls2txt/bestfit.c
5*c2c66affSColin Finck  * COPYRIGHT:   Copyright 2016 Dmitry Chapyshev <dmitry@reactos.org>
6*c2c66affSColin Finck  */
7*c2c66affSColin Finck 
8*c2c66affSColin Finck #include "precomp.h"
9*c2c66affSColin Finck 
10*c2c66affSColin Finck static HANDLE
BestFit_CreateFile(const WCHAR * pszFile)11*c2c66affSColin Finck BestFit_CreateFile(const WCHAR *pszFile)
12*c2c66affSColin Finck {
13*c2c66affSColin Finck     DWORD dwBytesWritten;
14*c2c66affSColin Finck     HANDLE hFile;
15*c2c66affSColin Finck 
16*c2c66affSColin Finck     hFile = CreateFileW(pszFile,
17*c2c66affSColin Finck                         GENERIC_WRITE,
18*c2c66affSColin Finck                         FILE_SHARE_READ | FILE_SHARE_WRITE,
19*c2c66affSColin Finck                         NULL,
20*c2c66affSColin Finck                         CREATE_ALWAYS,
21*c2c66affSColin Finck                         FILE_ATTRIBUTE_NORMAL,
22*c2c66affSColin Finck                         NULL);
23*c2c66affSColin Finck     if (hFile != INVALID_HANDLE_VALUE)
24*c2c66affSColin Finck     {
25*c2c66affSColin Finck         /* Write UTF-8 BOM */
26*c2c66affSColin Finck         WriteFile(hFile, "\xEF\xBB\xBF", 3, &dwBytesWritten, NULL);
27*c2c66affSColin Finck     }
28*c2c66affSColin Finck 
29*c2c66affSColin Finck     return hFile;
30*c2c66affSColin Finck }
31*c2c66affSColin Finck 
32*c2c66affSColin Finck static VOID
BestFit_CloseFile(HANDLE hFile)33*c2c66affSColin Finck BestFit_CloseFile(HANDLE hFile)
34*c2c66affSColin Finck {
35*c2c66affSColin Finck     CloseHandle(hFile);
36*c2c66affSColin Finck }
37*c2c66affSColin Finck 
38*c2c66affSColin Finck static CHAR*
UTF8fromUNICODE(const WCHAR * pszInput,PSIZE_T Size)39*c2c66affSColin Finck UTF8fromUNICODE(const WCHAR *pszInput, PSIZE_T Size)
40*c2c66affSColin Finck {
41*c2c66affSColin Finck     ULONG Length;
42*c2c66affSColin Finck     CHAR *pszOutput;
43*c2c66affSColin Finck 
44*c2c66affSColin Finck     if (!pszInput || !Size) return NULL;
45*c2c66affSColin Finck 
46*c2c66affSColin Finck     Length = WideCharToMultiByte(CP_UTF8, 0, pszInput, -1, NULL, 0, NULL, NULL);
47*c2c66affSColin Finck 
48*c2c66affSColin Finck     *Size = Length * sizeof(CHAR);
49*c2c66affSColin Finck 
50*c2c66affSColin Finck     pszOutput = (CHAR *) malloc(*Size);
51*c2c66affSColin Finck     if (pszOutput)
52*c2c66affSColin Finck     {
53*c2c66affSColin Finck         WideCharToMultiByte(CP_UTF8, 0, pszInput, -1, pszOutput, Length, NULL, NULL);
54*c2c66affSColin Finck     }
55*c2c66affSColin Finck 
56*c2c66affSColin Finck     return pszOutput;
57*c2c66affSColin Finck }
58*c2c66affSColin Finck 
59*c2c66affSColin Finck static VOID
BestFit_Write(HANDLE hFile,const WCHAR * pszFormat,...)60*c2c66affSColin Finck BestFit_Write(HANDLE hFile, const WCHAR *pszFormat, ...)
61*c2c66affSColin Finck {
62*c2c66affSColin Finck     LARGE_INTEGER FileSize;
63*c2c66affSColin Finck     LARGE_INTEGER MoveTo;
64*c2c66affSColin Finck     LARGE_INTEGER NewPos;
65*c2c66affSColin Finck     DWORD dwBytesWritten;
66*c2c66affSColin Finck 
67*c2c66affSColin Finck     if (hFile == INVALID_HANDLE_VALUE)
68*c2c66affSColin Finck         return;
69*c2c66affSColin Finck 
70*c2c66affSColin Finck     MoveTo.QuadPart = 0;
71*c2c66affSColin Finck     if (!SetFilePointerEx(hFile, MoveTo, &NewPos, FILE_END))
72*c2c66affSColin Finck         return;
73*c2c66affSColin Finck 
74*c2c66affSColin Finck     if (!GetFileSizeEx(hFile, &FileSize))
75*c2c66affSColin Finck         return;
76*c2c66affSColin Finck 
77*c2c66affSColin Finck     if (LockFile(hFile, (DWORD_PTR)NewPos.QuadPart, 0, (DWORD_PTR)FileSize.QuadPart, 0))
78*c2c66affSColin Finck     {
79*c2c66affSColin Finck         WCHAR *pszString;
80*c2c66affSColin Finck         CHAR *pszUtf8;
81*c2c66affSColin Finck         va_list Args;
82*c2c66affSColin Finck         SIZE_T Size;
83*c2c66affSColin Finck 
84*c2c66affSColin Finck         va_start(Args, pszFormat);
85*c2c66affSColin Finck 
86*c2c66affSColin Finck         Size = (_vscwprintf(pszFormat, Args) + 1) * sizeof(WCHAR);
87*c2c66affSColin Finck         pszString = (WCHAR*) malloc(Size);
88*c2c66affSColin Finck 
89*c2c66affSColin Finck         if (!pszString)
90*c2c66affSColin Finck         {
91*c2c66affSColin Finck             UnlockFile(hFile, (DWORD_PTR)NewPos.QuadPart, 0, (DWORD_PTR)FileSize.QuadPart, 0);
92*c2c66affSColin Finck             va_end(Args);
93*c2c66affSColin Finck             return;
94*c2c66affSColin Finck         }
95*c2c66affSColin Finck 
96*c2c66affSColin Finck         StringCbVPrintfW(pszString, Size, pszFormat, Args);
97*c2c66affSColin Finck         va_end(Args);
98*c2c66affSColin Finck 
99*c2c66affSColin Finck         pszUtf8 = UTF8fromUNICODE(pszString, &Size);
100*c2c66affSColin Finck         if (pszUtf8)
101*c2c66affSColin Finck         {
102*c2c66affSColin Finck             WriteFile(hFile, pszUtf8, Size - sizeof(CHAR), &dwBytesWritten, NULL);
103*c2c66affSColin Finck             free(pszUtf8);
104*c2c66affSColin Finck         }
105*c2c66affSColin Finck 
106*c2c66affSColin Finck         free(pszString);
107*c2c66affSColin Finck 
108*c2c66affSColin Finck         UnlockFile(hFile, (DWORD_PTR)NewPos.QuadPart, 0, (DWORD_PTR)FileSize.QuadPart, 0);
109*c2c66affSColin Finck     }
110*c2c66affSColin Finck }
111*c2c66affSColin Finck 
112*c2c66affSColin Finck BOOL
BestFit_FromNLS(const WCHAR * pszNLSFile,const WCHAR * pszBestFitFile)113*c2c66affSColin Finck BestFit_FromNLS(const WCHAR *pszNLSFile, const WCHAR *pszBestFitFile)
114*c2c66affSColin Finck {
115*c2c66affSColin Finck     CPTABLEINFO CodePageTable;
116*c2c66affSColin Finck     PUSHORT CodePage;
117*c2c66affSColin Finck     HANDLE hFile;
118*c2c66affSColin Finck     USHORT CodePageChar;
119*c2c66affSColin Finck     ULONG UnicodeChar;
120*c2c66affSColin Finck 
121*c2c66affSColin Finck     CodePage = NLS_ReadFile(pszNLSFile, &CodePageTable);
122*c2c66affSColin Finck     if (CodePage == NULL)
123*c2c66affSColin Finck         return FALSE;
124*c2c66affSColin Finck 
125*c2c66affSColin Finck     hFile = BestFit_CreateFile(pszBestFitFile);
126*c2c66affSColin Finck     if (hFile == INVALID_HANDLE_VALUE)
127*c2c66affSColin Finck     {
128*c2c66affSColin Finck         free(CodePage);
129*c2c66affSColin Finck         return FALSE;
130*c2c66affSColin Finck     }
131*c2c66affSColin Finck 
132*c2c66affSColin Finck     /* The only field is the decimal windows code page number for this code page. */
133*c2c66affSColin Finck     BestFit_Write(hFile, L"CODEPAGE %u\r\n\r\n", CodePageTable.CodePage);
134*c2c66affSColin Finck 
135*c2c66affSColin Finck     BestFit_Write(hFile,
136*c2c66affSColin Finck                   L"CPINFO %u 0x%02X 0x%04X\r\n\r\n",
137*c2c66affSColin Finck                   /* "1" for a single byte code page, "2" for a double byte code page */
138*c2c66affSColin Finck                   CodePageTable.MaximumCharacterSize,
139*c2c66affSColin Finck                   /* Replacement characters for unassigned Unicode code points when
140*c2c66affSColin Finck                      written to this code page */
141*c2c66affSColin Finck                   CodePageTable.DefaultChar,
142*c2c66affSColin Finck                   /* Replacement characters for illegal or unassigned code page values
143*c2c66affSColin Finck                      when converting to Unicode. */
144*c2c66affSColin Finck                   CodePageTable.UniDefaultChar);
145*c2c66affSColin Finck 
146*c2c66affSColin Finck     /* This field contains the number of following records of code page to Unicode mappings. */
147*c2c66affSColin Finck     BestFit_Write(hFile, L"MBTABLE %u\r\n\r\n", NLS_RecordsCountForMBTable(&CodePageTable));
148*c2c66affSColin Finck 
149*c2c66affSColin Finck     for (CodePageChar = 0; CodePageChar <= 0xFF; CodePageChar++)
150*c2c66affSColin Finck     {
151*c2c66affSColin Finck         if (!NLS_IsDefaultCharForMB(&CodePageTable, CodePageChar))
152*c2c66affSColin Finck         {
153*c2c66affSColin Finck             WCHAR szCharName[MAX_STR_LEN] = { 0 };
154*c2c66affSColin Finck 
155*c2c66affSColin Finck             GetUName(CodePageTable.MultiByteTable[CodePageChar], szCharName);
156*c2c66affSColin Finck 
157*c2c66affSColin Finck             BestFit_Write(hFile,
158*c2c66affSColin Finck                           L"0x%02X 0x%04X ;%s\r\n",
159*c2c66affSColin Finck                           CodePageChar,
160*c2c66affSColin Finck                           CodePageTable.MultiByteTable[CodePageChar],
161*c2c66affSColin Finck                           szCharName);
162*c2c66affSColin Finck         }
163*c2c66affSColin Finck     }
164*c2c66affSColin Finck 
165*c2c66affSColin Finck     BestFit_Write(hFile, L"\r\n");
166*c2c66affSColin Finck 
167*c2c66affSColin Finck     if (NLS_IsGlyphTablePresent(&CodePageTable))
168*c2c66affSColin Finck     {
169*c2c66affSColin Finck         PUSHORT GlyphTable = CodePageTable.MultiByteTable + 256 + 1;
170*c2c66affSColin Finck 
171*c2c66affSColin Finck         BestFit_Write(hFile, L"GLYPHTABLE %u\r\n\r\n", NLS_RecordsCountForGlyphTable(&CodePageTable));
172*c2c66affSColin Finck 
173*c2c66affSColin Finck         for (CodePageChar = 0; CodePageChar <= 0xFF; CodePageChar++)
174*c2c66affSColin Finck         {
175*c2c66affSColin Finck             if (CodePageChar != CodePageTable.UniDefaultChar)
176*c2c66affSColin Finck             {
177*c2c66affSColin Finck                 WCHAR szCharName[MAX_STR_LEN] = { 0 };
178*c2c66affSColin Finck 
179*c2c66affSColin Finck                 GetUName(GlyphTable[CodePageChar], szCharName);
180*c2c66affSColin Finck 
181*c2c66affSColin Finck                 BestFit_Write(hFile,
182*c2c66affSColin Finck                               L"0x%02X 0x%04X ;%s\r\n",
183*c2c66affSColin Finck                               CodePageChar,
184*c2c66affSColin Finck                               GlyphTable[CodePageChar],
185*c2c66affSColin Finck                               szCharName);
186*c2c66affSColin Finck             }
187*c2c66affSColin Finck         }
188*c2c66affSColin Finck 
189*c2c66affSColin Finck         BestFit_Write(hFile, L"\r\n");
190*c2c66affSColin Finck     }
191*c2c66affSColin Finck 
192*c2c66affSColin Finck     if (NLS_IsDBCSCodePage(&CodePageTable))
193*c2c66affSColin Finck     {
194*c2c66affSColin Finck         PUSHORT LeadByteRanges = (PUSHORT)&CodePageTable.LeadByte[0];
195*c2c66affSColin Finck         USHORT Index;
196*c2c66affSColin Finck         USHORT LeadByte;
197*c2c66affSColin Finck 
198*c2c66affSColin Finck         BestFit_Write(hFile,
199*c2c66affSColin Finck                       L"DBCSRANGE %u ;%u DBCS Lead Byte Ranges\r\n\r\n",
200*c2c66affSColin Finck                       CodePageTable.DBCSRanges[0],
201*c2c66affSColin Finck                       CodePageTable.DBCSRanges[0]);
202*c2c66affSColin Finck 
203*c2c66affSColin Finck         for (Index = 0; Index < MAXIMUM_LEADBYTES / 2; Index++)
204*c2c66affSColin Finck         {
205*c2c66affSColin Finck             if (!LeadByteRanges[Index])
206*c2c66affSColin Finck                 continue;
207*c2c66affSColin Finck 
208*c2c66affSColin Finck             BestFit_Write(hFile,
209*c2c66affSColin Finck                           L"0x%X 0x%X ;Lead Byte Range %u\r\n\r\n",
210*c2c66affSColin Finck                           LOBYTE(LeadByteRanges[Index]),
211*c2c66affSColin Finck                           HIBYTE(LeadByteRanges[Index]),
212*c2c66affSColin Finck                           Index + 1);
213*c2c66affSColin Finck 
214*c2c66affSColin Finck             for (LeadByte = LOBYTE(LeadByteRanges[Index]);
215*c2c66affSColin Finck                  LeadByte <= HIBYTE(LeadByteRanges[Index]);
216*c2c66affSColin Finck                  LeadByte++)
217*c2c66affSColin Finck             {
218*c2c66affSColin Finck                 PUSHORT LeadByteInfo = CodePageTable.DBCSOffsets;
219*c2c66affSColin Finck 
220*c2c66affSColin Finck                 BestFit_Write(hFile,
221*c2c66affSColin Finck                               L"DBCSTABLE %u ;Range = %u, LeadByte = 0x%02X\r\n\r\n",
222*c2c66affSColin Finck                               NLS_RecordsCountForDBCSTable(&CodePageTable, LeadByte),
223*c2c66affSColin Finck                               Index + 1,
224*c2c66affSColin Finck                               LeadByte);
225*c2c66affSColin Finck 
226*c2c66affSColin Finck                 for (CodePageChar = 0; CodePageChar <= 0xFF; CodePageChar++)
227*c2c66affSColin Finck                 {
228*c2c66affSColin Finck                     USHORT Info = LeadByteInfo[LeadByte];
229*c2c66affSColin Finck 
230*c2c66affSColin Finck                     if (Info && LeadByteInfo[Info + CodePageChar] != CodePageTable.UniDefaultChar)
231*c2c66affSColin Finck                     {
232*c2c66affSColin Finck                         BestFit_Write(hFile,
233*c2c66affSColin Finck                                       L"0x%02X 0x%04X\r\n",
234*c2c66affSColin Finck                                       CodePageChar,
235*c2c66affSColin Finck                                       LeadByteInfo[Info + CodePageChar]);
236*c2c66affSColin Finck                     }
237*c2c66affSColin Finck                 }
238*c2c66affSColin Finck 
239*c2c66affSColin Finck                 BestFit_Write(hFile, L"\r\n");
240*c2c66affSColin Finck             }
241*c2c66affSColin Finck         }
242*c2c66affSColin Finck     }
243*c2c66affSColin Finck 
244*c2c66affSColin Finck     /* This field contains the number of records of Unicode to byte mappings. */
245*c2c66affSColin Finck     BestFit_Write(hFile, L"WCTABLE %u\r\n\r\n", NLS_RecordsCountForUnicodeTable(&CodePageTable));
246*c2c66affSColin Finck 
247*c2c66affSColin Finck     for (UnicodeChar = 0; UnicodeChar <= 0xFFFF; UnicodeChar++)
248*c2c66affSColin Finck     {
249*c2c66affSColin Finck         if (!NLS_IsDefaultCharForUnicode(&CodePageTable, UnicodeChar))
250*c2c66affSColin Finck         {
251*c2c66affSColin Finck             WCHAR szCharName[MAX_STR_LEN] = { 0 };
252*c2c66affSColin Finck 
253*c2c66affSColin Finck             GetUName(UnicodeChar, szCharName);
254*c2c66affSColin Finck 
255*c2c66affSColin Finck             if (NLS_IsDBCSCodePage(&CodePageTable))
256*c2c66affSColin Finck             {
257*c2c66affSColin Finck                 PUSHORT MultiByteTable = (PUSHORT)CodePageTable.WideCharTable;
258*c2c66affSColin Finck 
259*c2c66affSColin Finck                 BestFit_Write(hFile,
260*c2c66affSColin Finck                               L"0x%04X 0x%04X ;%s\r\n",
261*c2c66affSColin Finck                               UnicodeChar,
262*c2c66affSColin Finck                               MultiByteTable[UnicodeChar],
263*c2c66affSColin Finck                               szCharName);
264*c2c66affSColin Finck             }
265*c2c66affSColin Finck             else
266*c2c66affSColin Finck             {
267*c2c66affSColin Finck                 PUCHAR SingleByteTable = (PUCHAR)CodePageTable.WideCharTable;
268*c2c66affSColin Finck 
269*c2c66affSColin Finck                 BestFit_Write(hFile,
270*c2c66affSColin Finck                               L"0x%04X 0x%02X ;%s\r\n",
271*c2c66affSColin Finck                               UnicodeChar,
272*c2c66affSColin Finck                               SingleByteTable[UnicodeChar],
273*c2c66affSColin Finck                               szCharName);
274*c2c66affSColin Finck             }
275*c2c66affSColin Finck         }
276*c2c66affSColin Finck     }
277*c2c66affSColin Finck 
278*c2c66affSColin Finck     /* This tag marks the end of the code page data. Anything after this marker is ignored. */
279*c2c66affSColin Finck     BestFit_Write(hFile, L"\r\nENDCODEPAGE\r\n");
280*c2c66affSColin Finck 
281*c2c66affSColin Finck     BestFit_CloseFile(hFile);
282*c2c66affSColin Finck     free(CodePage);
283*c2c66affSColin Finck 
284*c2c66affSColin Finck     return TRUE;
285*c2c66affSColin Finck }
286