:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / tools / create_nls / create_nls.c
1 /*
2  * Tool for creating NT-like NLS files for Unicode <-> Codepage conversions.
3  * Tool for creating NT-like l_intl.nls file for case mapping of unicode
4  * characters.
5  * Copyright 2000 Timoshkov Dmitry
6  * Copyright 2001 Matei Alexandru
7  *
8  * Sources of information:
9  * Andrew Kozin's YAW project http://www.chat.ru/~stanson/yaw_en.html
10  * Ove Kõven's investigations http://www.ping.uio.no/~ovehk/nls
11  */
12 #include <windows.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <malloc.h>
16 #include <string.h>
17 #include <ctype.h>
18
19 static const WCHAR * const uprtable[256];
20 static const WCHAR * const lwrtable[256];
21
22 #define NLSDIR "../../media/nls"
23 #define LIBDIR "unicode.org/"
24
25 typedef struct {
26     WORD wSize; /* in words 0x000D */
27     WORD CodePage;
28     WORD MaxCharSize; /* 1 or 2 */
29     BYTE DefaultChar[MAX_DEFAULTCHAR];
30     WCHAR UnicodeDefaultChar;
31     WCHAR unknown1;
32     WCHAR unknown2;
33     BYTE LeadByte[MAX_LEADBYTES];
34 } __attribute__((packed)) NLS_FILE_HEADER;
35
36 /*
37 Support for translation from the multiple unicode chars
38 to the single code page char.
39
40 002D;HYPHEN-MINUS;Pd;0;ET;;;;;N;;;;;
41 00AD;SOFT HYPHEN;Pd;0;ON;;;;;N;;;;;
42 2010;HYPHEN;Pd;0;ON;;;;;N;;;;;
43 2011;NON-BREAKING HYPHEN;Pd;0;ON;<noBreak> 2010;;;;N;;;;;
44 2013;EN DASH;Pd;0;ON;;;;;N;;;;;
45 2014;EM DASH;Pd;0;ON;;;;;N;;;;;
46 2015;HORIZONTAL BAR;Pd;0;ON;;;;;N;QUOTATION DASH;;;;
47 */
48
49 /* HYPHEN-MINUS aliases */
50 static WCHAR hyphen_aliases[] = {0x00AD,0x2010,0x2011,0x2013,0x2014,0x2015,0};
51
52 static struct {
53     WCHAR cp_char;
54     WCHAR *alias; /* must be 0 terminated */
55 } u2cp_alias[] = {
56 /* HYPHEN-MINUS aliases */
57 {0x002D, hyphen_aliases}
58 };
59
60 static void patch_aliases(void *u2cp, CPINFOEXA *cpi)
61 {
62     int i, j;
63     WCHAR *wc, *alias;
64     BYTE *c;
65
66     if(cpi->MaxCharSize == 2) {
67         wc = (WCHAR *)u2cp;
68         for(i = 0; i < 65536; i++) {
69             for(j = 0; j < sizeof(u2cp_alias)/sizeof(u2cp_alias[0]); j++) {
70                 alias = u2cp_alias[j].alias;
71                 while(*alias) {
72                     if(*alias == i && wc[i] == *(WCHAR *)cpi->DefaultChar) {
73                         wc[i] = u2cp_alias[j].cp_char;
74                     }
75                     alias++;
76                 }
77             }
78         }
79     }
80     else {
81         c = (BYTE *)u2cp;
82         for(i = 0; i < 65536; i++) {
83             for(j = 0; j < sizeof(u2cp_alias)/sizeof(u2cp_alias[0]); j++) {
84                 alias = u2cp_alias[j].alias;
85                 while(*alias) {
86                     if(*alias == i && c[i] == cpi->DefaultChar[0] && u2cp_alias[j].cp_char < 256) {
87                         c[i] = (BYTE)u2cp_alias[j].cp_char;
88                     }
89                     alias++;
90                 }
91             }
92         }
93     }
94 }
95
96 static BOOL write_unicode2cp_table(FILE *out, CPINFOEXA *cpi, WCHAR *table)
97 {
98     void *u2cp;
99     WCHAR *wc;
100     CHAR *c;
101     int i;
102     BOOL ret = TRUE;
103
104     u2cp = malloc(cpi->MaxCharSize * 65536);
105     if(!u2cp) {
106         printf("Not enough memory for Unicode to Codepage table\n");
107         return FALSE;
108     }
109
110     if(cpi->MaxCharSize == 2) {
111         wc = (WCHAR *)u2cp;
112         for(i = 0; i < 65536; i++)
113             wc[i] = *(WCHAR *)cpi->DefaultChar;
114
115         for(i = 0; i < 65536; i++)
116             if (table[i] != '?')
117                 wc[table[i]] = (WCHAR)i;
118     }
119     else {
120         c = (CHAR *)u2cp;
121         for(i = 0; i < 65536; i++)
122             c[i] = cpi->DefaultChar[0];
123
124         for(i = 0; i < 256; i++)
125             if (table[i] != '?')
126                 c[table[i]] = (CHAR)i;
127     }
128
129     patch_aliases(u2cp, cpi);
130
131     if(fwrite(u2cp, 1, cpi->MaxCharSize * 65536, out) != cpi->MaxCharSize * 65536)
132         ret = FALSE;
133
134     free(u2cp);
135
136     return ret;
137 }
138
139 static BOOL write_lb_ranges(FILE *out, CPINFOEXA *cpi, WCHAR *table)
140 {
141     WCHAR sub_table[256];
142     WORD offset, offsets[256];
143     int i, j, range;
144
145     memset(offsets, 0, sizeof(offsets));
146
147     offset = 0;
148
149     for(i = 0; i < MAX_LEADBYTES; i += 2) {
150         for(range = cpi->LeadByte[i]; range != 0 && range <= cpi->LeadByte[i + 1]; range++) {
151             offset += 256;
152             offsets[range] = offset;
153         }
154     }
155
156     if(fwrite(offsets, 1, sizeof(offsets), out) != sizeof(offsets))
157         return FALSE;
158
159     for(i = 0; i < MAX_LEADBYTES; i += 2) {
160         for(range = cpi->LeadByte[i]; range != 0 && range <= cpi->LeadByte[i + 1]; range++) {
161             /*printf("Writing sub table for LeadByte %02X\n", range);*/
162             for(j = MAKEWORD(0, range); j <= MAKEWORD(0xFF, range); j++) {
163                 sub_table[j - MAKEWORD(0, range)] = table[j];
164             }
165
166             if(fwrite(sub_table, 1, sizeof(sub_table), out) != sizeof(sub_table))
167                 return FALSE;
168         }
169     }
170
171     return TRUE;
172 }
173
174 static BOOL create_nls_file(char *name, CPINFOEXA *cpi, WCHAR *table, WCHAR *oemtable)
175 {
176     FILE *out;
177     NLS_FILE_HEADER nls;
178     WORD wValue, number_of_lb_ranges, number_of_lb_subtables, i;
179
180     printf("Creating NLS table \"%s\"\n", name);
181
182     if(!(out = fopen(name, "wb"))) {
183         printf("Could not create file \"%s\"\n", name);
184         return FALSE;
185     }
186
187     memset(&nls, 0, sizeof(nls));
188
189     nls.wSize = sizeof(nls) / sizeof(WORD);
190     nls.CodePage = cpi->CodePage;
191     nls.MaxCharSize = cpi->MaxCharSize;
192     memcpy(nls.DefaultChar, cpi->DefaultChar, MAX_DEFAULTCHAR);
193     nls.UnicodeDefaultChar = cpi->UnicodeDefaultChar;
194     nls.unknown1 = '?';
195     nls.unknown2 = '?';
196     memcpy(nls.LeadByte, cpi->LeadByte, MAX_LEADBYTES);
197
198     if(fwrite(&nls, 1, sizeof(nls), out) != sizeof(nls)) {
199         fclose(out);
200         printf("Could not write to file \"%s\"\n", name);
201         return FALSE;
202     }
203
204     number_of_lb_ranges = 0;
205     number_of_lb_subtables = 0;
206
207     for(i = 0; i < MAX_LEADBYTES; i += 2) {
208         if(cpi->LeadByte[i] != 0 && cpi->LeadByte[i + 1] > cpi->LeadByte[i]) {
209             number_of_lb_ranges++;
210             number_of_lb_subtables += cpi->LeadByte[i + 1] - cpi->LeadByte[i] + 1;
211         }
212     }
213
214     /*printf("Number of LeadByte ranges %d\n", number_of_lb_ranges);*/
215     /*printf("Number of LeadByte subtables %d\n", number_of_lb_subtables);*/
216
217     /* Calculate offset to Unicode to CP table in words:
218      *  1. (256 * sizeof(WORD)) primary CP to Unicode table +
219      *  2. (WORD) optional OEM glyph table size in words +
220      *  3. OEM glyph table size in words * sizeof(WORD) +
221      *  4. (WORD) Number of DBCS LeadByte ranges + 
222      *  5. if (Number of DBCS LeadByte ranges != 0) 256 * sizeof(WORD) offsets of lead byte sub tables
223      *  6. (Number of DBCS LeadByte sub tables * 256 * sizeof(WORD)) LeadByte sub tables +
224      *  7. (WORD) Unknown flag
225      */
226
227     wValue = (256 * sizeof(WORD) + /* 1 */
228               sizeof(WORD) + /* 2 */
229               ((oemtable !=NULL) ? (256 * sizeof(WORD)) : 0) + /* 3 */
230               sizeof(WORD) + /* 4 */
231               ((number_of_lb_subtables != 0) ? 256 * sizeof(WORD) : 0) + /* 5 */
232               number_of_lb_subtables * 256 * sizeof(WORD) + /* 6 */
233               sizeof(WORD) /* 7 */
234               ) / sizeof(WORD);
235
236     /* offset of Unicode to CP table in words */
237     fwrite(&wValue, 1, sizeof(wValue), out);
238
239     /* primary CP to Unicode table */
240     if(fwrite(table, 1, 256 * sizeof(WCHAR), out) != 256 * sizeof(WCHAR)) {
241         fclose(out);
242         printf("Could not write to file \"%s\"\n", name);
243         return FALSE;
244     }
245
246     /* optional OEM glyph table size in words */
247     wValue = (oemtable != NULL) ? (256 * sizeof(WORD)) : 0;
248     fwrite(&wValue, 1, sizeof(wValue), out);
249
250     /* optional OEM to Unicode table */
251     if (oemtable) {
252         if(fwrite(oemtable, 1, 256 * sizeof(WCHAR), out) != 256 * sizeof(WCHAR)) {
253             fclose(out);
254             printf("Could not write to file \"%s\"\n", name);
255             return FALSE;
256         }
257     }
258
259     /* Number of DBCS LeadByte ranges */
260     fwrite(&number_of_lb_ranges, 1, sizeof(number_of_lb_ranges), out);
261
262     /* offsets of lead byte sub tables and lead byte sub tables */
263     if(number_of_lb_ranges > 0) {
264         if(!write_lb_ranges(out, cpi, table)) {
265             fclose(out);
266             printf("Could not write to file \"%s\"\n", name);
267             return FALSE;
268         }
269     }
270
271     /* Unknown flag */
272     wValue = 0;
273     fwrite(&wValue, 1, sizeof(wValue), out);
274
275     if(!write_unicode2cp_table(out, cpi, table)) {
276         fclose(out);
277         printf("Could not write to file \"%s\"\n", name);
278         return FALSE;
279     }
280
281     fclose(out);
282     return TRUE;
283 }
284
285 /* correct the codepage information such as default chars */
286 static void patch_codepage_info(CPINFOEXA *cpi)
287 {
288     /* currently nothing */
289 }
290
291 static WCHAR *Load_CP2Unicode_Table(char *table_name, UINT cp, CPINFOEXA *cpi)
292 {
293     char buf[256];
294     char *p;
295     DWORD n, value;
296     FILE *file;
297     WCHAR *table;
298     int lb_ranges, lb_range_started, line;
299
300     printf("Loading translation table \"%s\"\n", table_name);
301     
302     /* Init to default values */
303     memset(cpi, 0, sizeof(CPINFOEXA));
304     cpi->CodePage = cp;
305     *(WCHAR *)cpi->DefaultChar = '?';
306     cpi->MaxCharSize = 1;
307     cpi->UnicodeDefaultChar = '?';
308
309     patch_codepage_info(cpi);
310
311     table = (WCHAR *)malloc(sizeof(WCHAR) * 65536);
312     if(!table) {
313         printf("Not enough memory for Codepage to Unicode table\n");
314         return NULL;
315     }
316
317     for(n = 0; n < 256; n++)
318         table[n] = (WCHAR)n;
319
320     for(n = 256; n < 65536; n++)
321         table[n] = cpi->UnicodeDefaultChar;
322
323     file = fopen(table_name, "r");
324     if(file == NULL) {
325         free(table);
326         return NULL;
327     }
328
329     line = 0;
330     lb_ranges = 0;
331     lb_range_started = 0;
332
333     while(fgets(buf, sizeof(buf), file)) {
334         line++;
335         p = buf;
336         while(isspace(*p)) p++;
337
338         if(!*p || p[0] == '#')
339             continue;
340
341         n = strtol(p, &p, 0);
342         if(n > 0xFFFF) {
343             printf("Line %d: Entry 0x%06lX: File \"%s\" corrupted\n", line, n, table_name);
344             continue;
345         }
346
347         if(n > 0xFF && cpi->MaxCharSize != 2) {
348             /*printf("Line %d: Entry 0x%04lX: Switching to DBCS\n", line, n);*/
349             cpi->MaxCharSize = 2;
350         }
351
352         while(isspace(*p)) p++;
353
354         if(!*p || p[0] == '#') {
355             /*printf("Line %d: Entry 0x%02lX has no Unicode value\n", line, n);*/
356         }
357         else {
358             value = strtol(p, &p, 0);
359             if(value > 0xFFFF) {
360                 printf("Line %d: Entry 0x%06lX unicode value: File \"%s\" corrupted\n", line, n, table_name);
361             }
362             table[n] = (WCHAR)value;
363         }
364
365         /* wait for comment */
366         while(*p && *p != '#') p++;
367
368         if(*p == '#' && strstr(p, "DBCS LEAD BYTE")) {
369             /*printf("Line %d, entry 0x%02lX DBCS LEAD BYTE\n", line, n);*/
370             if(n > 0xFF) {
371                 printf("Line %d: Entry 0x%04lX: Error: DBCS lead byte overflowed\n", line, n);
372                 continue;
373             }
374
375             table[n] = (WCHAR)0;
376
377             if(lb_range_started) {
378                 cpi->LeadByte[(lb_ranges - 1) * 2 + 1] = (BYTE)n;
379             }
380             else {
381                 /*printf("Line %d: Starting new DBCS lead byte range, entry 0x%02lX\n", line, n);*/
382                 if(lb_ranges < MAX_LEADBYTES/2) {
383                     lb_ranges++;
384                     lb_range_started = 1;
385                     cpi->LeadByte[(lb_ranges - 1) * 2] = (BYTE)n;
386                 }
387                 else
388                     printf("Line %d: Error: could not start new lead byte range\n", line);
389             }
390         }
391         else {
392             if(lb_range_started)
393                 lb_range_started = 0;
394         }
395     }
396
397     fclose(file);
398
399     return table;
400 }
401
402 static WCHAR *Load_OEM2Unicode_Table(char *table_name, WCHAR *def_table, UINT cp, CPINFOEXA *cpi)
403 {
404     char buf[256];
405     char *p;
406     DWORD n, value;
407     FILE *file;
408     WCHAR *table;
409     int line;
410
411     printf("Loading oem glyph table \"%s\"\n", table_name);
412     
413     table = (WCHAR *)malloc(sizeof(WCHAR) * 65536);
414     if(!table) {
415         printf("Not enough memory for Codepage to Unicode table\n");
416         return NULL;
417     }
418
419     memcpy(table, def_table, 65536 * sizeof(WCHAR));
420
421     file = fopen(table_name, "r");
422     if(file == NULL) {
423         free(table);
424         return NULL;
425     }
426
427     while(fgets(buf, sizeof(buf), file)) {
428         line++;
429         p = buf;
430         while(isspace(*p)) p++;
431
432         if(!*p || p[0] == '#')
433             continue;
434
435         value = strtol(p, &p, 16);
436         if(value > 0xFFFF) {
437             printf("Line %d: Entry 0x%06lX: File \"%s\" corrupted\n", line, value, table_name);
438             continue;
439         }
440
441         while(isspace(*p)) p++;
442
443         if(!*p || p[0] == '#') {
444             /*printf("Line %d: Entry 0x%02lX has no Unicode value\n", line, n);*/
445             continue;
446         }
447         else {
448             n = strtol(p, &p, 16);
449             if(n > 0xFFFF) {
450                 printf("Line %d: Entry 0x%06lX unicode value: File \"%s\" corrupted\n", line, value, table_name);
451                 continue;
452             }
453         }
454
455         if (cpi->CodePage == 864) {
456             while(isspace(*p)) p++;
457
458             if(!*p || p[0] == '#' || p[0] == '-') {
459                 /*printf("Line %d: Entry 0x%02lX has no Unicode value\n", line, n);*/
460                 continue;
461             }
462             else {
463                 n = strtol(p, &p, 16);
464                 if(n > 0xFFFF) {
465                     printf("Line %d: Entry 0x%06lX oem value: File \"%s\" corrupted\n", line, value, table_name);
466                 }
467                 continue;
468             }
469         }
470
471         table[n] = (WCHAR)value;
472     }
473
474     fclose(file);
475
476     return table;
477 }
478
479 int write_nls_files()
480 {
481     WCHAR *table;
482     WCHAR *oemtable;
483     char nls_filename[256];
484     CPINFOEXA cpi;
485     int i;
486     struct code_page {
487         UINT cp;
488         BOOL oem;
489         char *table_filename;
490         char *comment;
491     } pages[] = {
492         {37,  FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/EBCDIC/CP037.TXT", "IBM EBCDIC US Canada"},
493         {424, FALSE, LIBDIR"MAPPINGS/VENDORS/MISC/CP424.TXT", "IBM EBCDIC Hebrew"},
494         {437, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP437.TXT", "OEM United States"},
495         {500, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/EBCDIC/CP500.TXT", "IBM EBCDIC International"},
496         /*{708, FALSE, "", "Arabic ASMO"},*/
497         /*{720, FALSE, "", "Arabic Transparent ASMO"},*/
498         {737, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP737.TXT", "OEM Greek 437G"},
499         {775, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP775.TXT", "OEM Baltic"},
500         {850, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP850.TXT", "OEM Multilingual Latin 1"},
501         {852, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP852.TXT", "OEM Slovak Latin 2"},
502         {855, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP855.TXT", "OEM Cyrillic" },
503         {856, TRUE,  LIBDIR"MAPPINGS/VENDORS/MISC/CP856.TXT", "Hebrew PC"},
504         {857, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP857.TXT", "OEM Turkish"},
505         {860, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP860.TXT", "OEM Portuguese"},
506         {861, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP861.TXT", "OEM Icelandic"},
507         {862, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP862.TXT", "OEM Hebrew"},
508         {863, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP863.TXT", "OEM Canadian French"},
509         {864, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP864.TXT", "OEM Arabic"},
510         {865, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP865.TXT", "OEM Nordic"},
511         {866, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP866.TXT", "OEM Russian"},
512         {869, TRUE,  LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP869.TXT", "OEM Greek"},
513         /*{870, FALSE, "", "IBM EBCDIC Multilingual/ROECE (Latin 2)"},*/
514         {874, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/PC/CP874.TXT", "ANSI/OEM Thai"},
515         {875, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/EBCDIC/CP875.TXT", "IBM EBCDIC Greek"},
516         {878, FALSE, LIBDIR"MAPPINGS/VENDORS/MISC/KOI8-R.TXT", "Russian KOI8"},
517         {932, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/WINDOWS/CP932.TXT", "ANSI/OEM Japanese Shift-JIS"},
518         {936, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/WINDOWS/CP936.TXT", "ANSI/OEM Simplified Chinese GBK"},
519         {949, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/WINDOWS/CP949.TXT", "ANSI/OEM Korean Unified Hangul"},
520         {950, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/WINDOWS/CP950.TXT", "ANSI/OEM Traditional Chinese Big5"},
521         {1006, FALSE, LIBDIR"MAPPINGS/VENDORS/MISC/CP1006.TXT", "IBM Arabic"},
522         {1026, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/EBCDIC/CP1026.TXT", "IBM EBCDIC Latin 5 Turkish"},
523         {1250, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1250.TXT", "ANSI Eastern Europe"},
524         {1251, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1251.TXT", "ANSI Cyrillic"},
525         {1252, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT", "ANSI Latin 1"},
526         {1253, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1253.TXT", "ANSI Greek"},
527         {1254, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1254.TXT", "ANSI Turkish"},
528         {1255, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1255.TXT", "ANSI Hebrew"},
529         {1256, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1256.TXT", "ANSI Arabic"},
530         {1257, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1257.TXT", "ANSI Baltic"},
531         {1258, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1258.TXT", "ANSI/OEM Viet Nam"},
532         {10000, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/MAC/ROMAN.TXT", "Mac Roman"},
533         {10006, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/MAC/GREEK.TXT", "Mac Greek"},
534         {10007, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/MAC/CYRILLIC.TXT", "Mac Cyrillic"},
535         {10029, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/MAC/LATIN2.TXT", "Mac Latin 2"},
536         {10079, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/MAC/ICELAND.TXT", "Mac Icelandic"},
537         {10081, FALSE, LIBDIR"MAPPINGS/VENDORS/MICSFT/MAC/TURKISH.TXT", "Mac Turkish"},
538         /*{20000, FALSE, "", "CNS Taiwan"},*/
539         /*{20001, FALSE, "", "TCA Taiwan"},*/
540         /*{20002, FALSE, "", "Eten Taiwan"},*/
541         /*{20003, FALSE, "", "IBM5550 Taiwan"},*/
542         /*{20004, FALSE, "", "TeleText Taiwan"},*/
543         /*{20005, FALSE, "", "Wang Taiwan"},*/
544         /*{20105, FALSE, "", "IA5 IRV International Alphabet No.5"},*/
545         /*{20106, FALSE, "", "IA5 German"},*/
546         /*{20107, FALSE, "", "IA5 Swedish"},*/
547         /*{20108, FALSE, "", "IA5 Norwegian"},*/
548         /*{20127, FALSE, "", "US ASCII"},*/
549         /*{20261, FALSE, "", "T.61"},*/
550         /*{20269, FALSE, "", "ISO 6937 NonSpacing Accent"},*/
551         /*{20273, FALSE, "", "IBM EBCDIC Germany"},*/
552         /*{20277, FALSE, "", "IBM EBCDIC Denmark/Norway"},*/
553         /*{20278, FALSE, "", "IBM EBCDIC Finland/Sweden"},*/
554         /*{20280, FALSE, "", "IBM EBCDIC Italy"},*/
555         /*{20284, FALSE, "", "IBM EBCDIC Latin America/Spain"},*/
556         /*{20285, FALSE, "", "IBM EBCDIC United Kingdom"},*/
557         /*{20290, FALSE, "", "IBM EBCDIC Japanese Katakana Extended"},*/
558         /*{20297, FALSE, "", "IBM EBCDIC France"},*/
559         /*{20420, FALSE, "", "IBM EBCDIC Arabic"},*/
560         /*{20423, FALSE, "", "IBM EBCDIC Greek"},*/
561         /*{20424, FALSE, "", "IBM EBCDIC Hebrew"},*/
562         /*{20833, FALSE, "", "IBM EBCDIC Korean Extended"},*/
563         /*{20838, FALSE, "", "IBM EBCDIC Thai"},*/
564         /*{20871, FALSE, "", "IBM EBCDIC Icelandic"},*/
565         /*{20880, FALSE, "", "IBM EBCDIC Cyrillic (Russian)"},*/
566         {20866, FALSE, LIBDIR"MAPPINGS/VENDORS/MISC/KOI8-R.TXT", "Russian KOI8"},
567         /*{20905, FALSE, "", "IBM EBCDIC Turkish"},*/
568         /*{21025, FALSE, "", "IBM EBCDIC Cyrillic (Serbian, Bulgarian)"},*/
569         /*{21027, FALSE, "", "Ext Alpha Lowercase"},*/
570         {28591, FALSE, LIBDIR"MAPPINGS/ISO8859/8859-1.TXT", "ISO 8859-1 Latin 1"},
571         {28592, FALSE, LIBDIR"MAPPINGS/ISO8859/8859-2.TXT", "ISO 8859-2 Eastern Europe"},
572         {28593, FALSE, LIBDIR"MAPPINGS/ISO8859/8859-3.TXT", "ISO 8859-3 Turkish"},
573         {28594, FALSE, LIBDIR"MAPPINGS/ISO8859/8859-4.TXT", "ISO 8859-4 Baltic"},
574         {28595, FALSE, LIBDIR"MAPPINGS/ISO8859/8859-5.TXT", "ISO 8859-5 Cyrillic"},
575         {28596, FALSE, LIBDIR"MAPPINGS/ISO8859/8859-6.TXT", "ISO 8859-6 Arabic"},
576         {28597, FALSE, LIBDIR"MAPPINGS/ISO8859/8859-7.TXT", "ISO 8859-7 Greek"},
577         {28598, FALSE, LIBDIR"MAPPINGS/ISO8859/8859-8.TXT", "ISO 8859-8 Hebrew"},
578         {28599, FALSE, LIBDIR"MAPPINGS/ISO8859/8859-9.TXT", "ISO 8859-9 Latin 5"}
579     };
580
581     for(i = 0; i < sizeof(pages)/sizeof(pages[0]); i++) {
582         table = Load_CP2Unicode_Table(pages[i].table_filename, pages[i].cp, &cpi);
583         if(!table) {
584             printf("Could not load \"%s\" (%s)\n", pages[i].table_filename, pages[i].comment);
585             continue;
586         }
587
588         if (pages[i].oem) {
589             oemtable = Load_OEM2Unicode_Table(LIBDIR"MAPPINGS/VENDORS/MISC/IBMGRAPH.TXT", table, pages[i].cp, &cpi);
590             if(!oemtable) {
591                 printf("Could not load \"%s\" (%s)\n", LIBDIR"MAPPINGS/VENDORS/MISC/IBMGRAPH.TXT", "IBM OEM glyph table");
592                 continue;
593             }
594         }
595
596         sprintf(nls_filename, "%s/c_%03d.nls", NLSDIR, cpi.CodePage);
597         if(!create_nls_file(nls_filename, &cpi, table, pages[i].oem ? oemtable : NULL)) {
598             printf("Could not write \"%s\" (%s)\n", nls_filename, pages[i].comment);
599         }
600
601         if (pages[i].oem)
602             free(oemtable);
603
604         free(table);
605     }
606
607     return 0;
608 }
609
610
611
612 static WORD *to_upper_org = NULL, *to_lower_org = NULL;
613
614 static WORD diffs[256];
615 static int number_of_diffs;
616
617 static WORD number_of_subtables_with_diffs;
618 /* pointers to subtables with 16 elements in each to the main table */
619 static WORD *subtables_with_diffs[4096];
620
621 static WORD number_of_subtables_with_offsets;
622 /* subtables with 16 elements  */
623 static WORD subtables_with_offsets[4096 * 16];
624
625 static void test_packed_table(WCHAR *table)
626 {
627     WCHAR test_str[] = L"This is an English text. Ïî-ðóññêè ÿ ïèñàòü óìåþ íåìíîæêî. 1234567890";
628     //WORD diff, off;
629     //WORD *sub_table;
630     DWORD i, len;
631
632     len = lstrlenW(test_str);
633
634     for(i = 0; i < len + 1; i++) {
635         /*off = table[HIBYTE(test_str[i])];
636
637         sub_table = table + off;
638         off = sub_table[LOBYTE(test_str[i]) >> 4];
639
640         sub_table = table + off;
641         off = LOBYTE(test_str[i]) & 0x0F;
642
643         diff = sub_table[off];
644
645         test_str[i] += diff;*/
646         test_str[i] += table[table[table[HIBYTE(test_str[i])] + (LOBYTE(test_str[i]) >> 4)] + (LOBYTE(test_str[i]) & 0x0F)];
647     }
648 /*
649     {
650         FILE *file;
651         static int n = 0;
652         char name[20];
653
654         sprintf(name, "text%02d.dat", n++);
655         file = fopen(name, "wb");
656         fwrite(test_str, len * sizeof(WCHAR), 1, file);
657         fclose(file);
658     }*/
659 }
660
661 static BOOL CreateCaseDiff(char *table_name)
662 {
663     char buf[256];
664     char *p;
665     WORD code, case_mapping;
666     FILE *file;
667     int line;
668
669     to_upper_org = (WORD *)calloc(65536, sizeof(WORD));
670     if(!to_upper_org) {
671         printf("Not enough memory for to upper table\n");
672         return FALSE;
673     }
674
675     to_lower_org = (WORD *)calloc(65536, sizeof(WORD));
676     if(!to_lower_org) {
677         printf("Not enough memory for to lower table\n");
678         return FALSE;
679     }
680
681     file = fopen(table_name, "r");
682     if(file == NULL) {
683         printf("Could not open file \"%s\"\n", table_name);
684         return FALSE;
685     }
686
687     line = 0;
688
689     while(fgets(buf, sizeof(buf), file)) {
690         line++;
691         p = buf;
692         while(*p && isspace(*p)) p++;
693
694         if(!*p)
695             continue;
696
697         /* 0. Code value */
698         code = (WORD)strtol(p, &p, 16);
699
700         //if(code != 0x9A0 && code != 0xBA0)
701             //continue;
702
703         while(*p && *p != ';') p++;
704         if(!*p)
705             continue;
706         p++;
707
708         /* 1. Character name */
709         while(*p && *p != ';') p++;
710         if(!*p)
711             continue;
712         p++;
713
714         /* 2. General Category */
715         while(*p && *p != ';') p++;
716         if(!*p)
717             continue;
718         p++;
719
720         /* 3. Canonical Combining Classes */
721         while(*p && *p != ';') p++;
722         if(!*p)
723             continue;
724         p++;
725
726         /* 4. Bidirectional Category */
727         while(*p && *p != ';') p++;
728         if(!*p)
729             continue;
730         p++;
731
732         /* 5. Character Decomposition Mapping */
733         while(*p && *p != ';') p++;
734         if(!*p)
735             continue;
736         p++;
737
738         /* 6. Decimal digit value */
739         while(*p && *p != ';') p++;
740         if(!*p)
741             continue;
742         p++;
743
744         /* 7. Digit value */
745         while(*p && *p != ';') p++;
746         if(!*p)
747             continue;
748         p++;
749
750         /* 8. Numeric value */
751         while(*p && *p != ';') p++;
752         if(!*p)
753             continue;
754         p++;
755
756         /* 9. Mirrored */
757         while(*p && *p != ';') p++;
758         if(!*p)
759             continue;
760         p++;
761
762         /* 10. Unicode 1.0 Name */
763         while(*p && *p != ';') p++;
764         if(!*p)
765             continue;
766         p++;
767
768         /* 11. 10646 comment field */
769         while(*p && *p != ';') p++;
770         if(!*p)
771             continue;
772         p++;
773
774         /* 12. Uppercase Mapping */
775         while(*p && isspace(*p)) p++;
776         if(!*p) continue;
777         if(*p != ';') {
778             case_mapping = (WORD)strtol(p, &p, 16);
779             to_upper_org[code] = case_mapping - code;
780             while(*p && *p != ';') p++;
781         }
782         else
783             p++;
784
785         /* 13. Lowercase Mapping */
786         while(*p && isspace(*p)) p++;
787         if(!*p) continue;
788         if(*p != ';') {
789             case_mapping = (WORD)strtol(p, &p, 16);
790             to_lower_org[code] = case_mapping - code;
791             while(*p && *p != ';') p++;
792         }
793         else
794             p++;
795
796         /* 14. Titlecase Mapping */
797         while(*p && *p != ';') p++;
798         if(!*p)
799             continue;
800         p++;
801     }
802
803     fclose(file);
804
805     return TRUE;
806 }
807
808 static int find_diff(WORD diff)
809 {
810     int i;
811
812     for(i = 0; i < number_of_diffs; i++) {
813         if(diffs[i] == diff)
814             return i;
815     }
816
817     return -1;
818 }
819
820 static WORD find_subtable_with_diffs(WORD *table, WORD *subtable)
821 {
822     WORD index;
823
824     for(index = 0; index < number_of_subtables_with_diffs; index++) {
825         if(memcmp(subtables_with_diffs[index], subtable, 16 * sizeof(WORD)) == 0) {
826             return index;
827         }
828     }
829
830     if(number_of_subtables_with_diffs >= 4096) {
831         printf("Could not add new subtable with diffs, storage is full\n");
832         return 0;
833     }
834
835     subtables_with_diffs[number_of_subtables_with_diffs] = subtable;
836     number_of_subtables_with_diffs++;
837
838     return index;
839 }
840
841 static WORD find_subtable_with_offsets(WORD *subtable)
842 {
843     WORD index;
844
845     for(index = 0; index < number_of_subtables_with_offsets; index++) {
846         if(memcmp(&subtables_with_offsets[index * 16], subtable, 16 * sizeof(WORD)) == 0) {
847             return index;
848         }
849     }
850
851     if(number_of_subtables_with_offsets >= 4096) {
852         printf("Could not add new subtable with offsets, storage is full\n");
853         return 0;
854     }
855
856     memcpy(&subtables_with_offsets[number_of_subtables_with_offsets * 16], subtable, 16 * sizeof(WORD));
857     number_of_subtables_with_offsets++;
858
859     return index;
860 }
861
862 static WORD *pack_table(WORD *table, WORD *packed_size_in_words)
863 {
864     WORD high, low4, index;
865     WORD main_index[256];
866     WORD temp_subtable[16];
867     WORD *packed_table;
868     WORD *subtable_src, *subtable_dst;
869
870     memset(subtables_with_diffs, 0, sizeof(subtables_with_diffs));
871     number_of_subtables_with_diffs = 0;
872
873     memset(subtables_with_offsets, 0, sizeof(subtables_with_offsets));
874     number_of_subtables_with_offsets = 0;
875
876     for(high = 0; high < 256; high++) {
877         for(low4 = 0; low4 < 256; low4 += 16) {
878             index = find_subtable_with_diffs(table, &table[MAKEWORD(low4, high)]);
879
880             temp_subtable[low4 >> 4] = index;
881         }
882
883         index = find_subtable_with_offsets(temp_subtable);
884         main_index[high] = index;
885     }
886
887     *packed_size_in_words = 0x100 + number_of_subtables_with_offsets * 16 + number_of_subtables_with_diffs * 16;
888     packed_table = calloc(*packed_size_in_words, sizeof(WORD));
889
890     /* fill main index according to the subtables_with_offsets */
891     for(high = 0; high < 256; high++) {
892         packed_table[high] = 0x100 + main_index[high] * 16;
893     }
894
895     //memcpy(sub_table, subtables_with_offsets, number_of_subtables_with_offsets * 16);
896
897     /* fill subtable index according to the subtables_with_diffs */
898     for(index = 0; index < number_of_subtables_with_offsets; index++) {
899         subtable_dst = packed_table + 0x100 + index * 16;
900         subtable_src = &subtables_with_offsets[index * 16];
901
902         for(low4 = 0; low4 < 16; low4++) {
903             subtable_dst[low4] = 0x100 + number_of_subtables_with_offsets * 16 + subtable_src[low4] * 16;
904         }
905     }
906
907
908     for(index = 0; index < number_of_subtables_with_diffs; index++) {
909         subtable_dst = packed_table + 0x100 + number_of_subtables_with_offsets * 16 + index * 16;
910         memcpy(subtable_dst, subtables_with_diffs[index], 16 * sizeof(WORD));
911
912     }
913
914
915     test_packed_table(packed_table);
916
917     return packed_table;
918 }
919
920 int write_casemap_file(void)
921 {
922     WORD packed_size_in_words, offset_to_next_table_in_words;
923     WORD *packed_table, value;
924     FILE *file;
925
926     if(!CreateCaseDiff(LIBDIR"UnicodeData.txt"))
927         return -1;
928
929     file = fopen(NLSDIR"/l_intl.nls", "wb");
930
931     /* write version number */
932     value = 1;
933     fwrite(&value, 1, sizeof(WORD), file);
934
935     /* pack upper case table */
936     packed_table = pack_table(to_upper_org, &packed_size_in_words);
937     offset_to_next_table_in_words = packed_size_in_words + 1;
938     fwrite(&offset_to_next_table_in_words, 1, sizeof(WORD), file);
939     /* write packed upper case table */
940     fwrite(packed_table, sizeof(WORD), packed_size_in_words, file);
941     free(packed_table);
942
943     /* pack lower case table */
944     packed_table = pack_table(to_lower_org, &packed_size_in_words);
945     offset_to_next_table_in_words = packed_size_in_words + 1;
946     fwrite(&offset_to_next_table_in_words, 1, sizeof(WORD), file);
947     /* write packed lower case table */
948     fwrite(packed_table, sizeof(WORD), packed_size_in_words, file);
949     free(packed_table);
950
951     fclose(file);
952
953     free(to_upper_org);
954     free(to_lower_org);
955
956     return 0;
957 }
958
959 int main()
960 {
961         write_nls_files();
962         write_casemap_file();
963
964         return 0;
965 }