a436b7343cd386fe38481a830fde8c8a42055be2
[reactos.git] / reactos / subsystems / mvdm / ntvdm / dos / dos32krnl / country.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/dos/dos32krnl/country.c
5 * PURPOSE: DOS32 Country support
6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
7 *
8 * NOTE: Support for default (english) language only.
9 * For other languages, please use COUNTRY.SYS
10 */
11
12 #include "ntvdm.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 /* PRIVATE VARIABLES **********************************************************/
18
19 /* CaseMap routine: should call INT 65h, AL=20h */
20 // ATM, just do nothing.
21 static const BYTE CaseMapRoutine[] =
22 {
23 0xCB // retf
24 };
25
26 #pragma pack(push, 1)
27
28 #define DATATABLE(name, type, len) \
29 typedef struct _##name \
30 { \
31 WORD Size; \
32 type Data[(len)]; \
33 } name
34
35 DATATABLE(UPPERCASE, CHAR, 0xFF-0x80+1);
36 DATATABLE(LOWERCASE, CHAR, 0xFF-0x00+1);
37 DATATABLE(FNAMETERM, BYTE, 22);
38 DATATABLE(COLLATE , BYTE, 0xFF-0x00+1);
39 DATATABLE(DBCSLEAD , WORD, 0x00+1);
40
41 typedef struct _COUNTRY_DATA
42 {
43 BYTE CaseMapRoutine[sizeof(CaseMapRoutine)];
44 UPPERCASE UpCaseTbl; // Used also for filename uppercase
45 LOWERCASE LoCaseTbl;
46 FNAMETERM FNameTermTbl;
47 COLLATE CollateTbl;
48 DBCSLEAD DBCSLeadTbl;
49 } COUNTRY_DATA, *PCOUNTRY_DATA;
50
51 #pragma pack(pop)
52
53 /* Global data contained in guest memory */
54 static WORD CountryDataSegment;
55 static PCOUNTRY_DATA CountryData;
56
57 WORD YesNoTable[2] = { MAKEWORD('Y', 0), MAKEWORD('N', 0) };
58
59 /*
60 * See: http://www.ctyme.com/intr/rb-3163.htm#Table1754
61 * http://www.ctyme.com/intr/rb-3164.htm
62 * http://www.ctyme.com/intr/rb-3166.htm
63 */
64
65 /* PRIVATE FUNCTIONS **********************************************************/
66
67 /* PUBLIC FUNCTIONS ***********************************************************/
68
69 WORD
70 DosGetCountryInfo(IN OUT PWORD CountryId,
71 OUT PDOS_COUNTRY_INFO CountryInfo)
72 {
73 INT Return;
74 DWORD NumVal;
75
76 Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IDATE | LOCALE_RETURN_NUMBER, // LOCALE_ILDATE | LOCALE_RETURN_NUMBER
77 (LPSTR)&NumVal,
78 sizeof(NumVal));
79 if (Return == 0) return LOWORD(GetLastError());
80 CountryInfo->DateTimeFormat = (WORD)NumVal;
81
82 Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SCURRENCY,
83 (LPSTR)&CountryInfo->CurrencySymbol,
84 sizeof(CountryInfo->CurrencySymbol));
85 if (Return == 0) return LOWORD(GetLastError());
86
87 Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND,
88 (LPSTR)&CountryInfo->ThousandSep,
89 sizeof(CountryInfo->ThousandSep));
90 if (Return == 0) return LOWORD(GetLastError());
91
92 Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
93 (LPSTR)&CountryInfo->DecimalSep,
94 sizeof(CountryInfo->DecimalSep));
95 if (Return == 0) return LOWORD(GetLastError());
96
97 Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDATE,
98 (LPSTR)&CountryInfo->DateSep,
99 sizeof(CountryInfo->DateSep));
100 if (Return == 0) return LOWORD(GetLastError());
101
102 Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_STIME,
103 (LPSTR)&CountryInfo->TimeSep,
104 sizeof(CountryInfo->TimeSep));
105 if (Return == 0) return LOWORD(GetLastError());
106
107 // NOTE: '4: Symbol replace decimal separator' is unsupported.
108 Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER,
109 (LPSTR)&NumVal,
110 sizeof(NumVal));
111 if (Return == 0) return LOWORD(GetLastError());
112 CountryInfo->CurrencyFormat = (BYTE)NumVal;
113
114 Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER, // LOCALE_IDIGITS | LOCALE_RETURN_NUMBER
115 (LPSTR)&NumVal,
116 sizeof(NumVal));
117 if (Return == 0) return LOWORD(GetLastError());
118 CountryInfo->CurrencyDigitsNum = (BYTE)NumVal;
119
120 Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ITIME | LOCALE_RETURN_NUMBER,
121 (LPSTR)&NumVal,
122 sizeof(NumVal));
123 if (Return == 0) return LOWORD(GetLastError());
124 CountryInfo->TimeFormat = (BYTE)NumVal;
125
126 CountryInfo->CaseMapPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, CaseMapRoutine), CountryDataSegment);
127
128 // CountryInfo->DataListSep;
129
130 return ERROR_SUCCESS;
131 }
132
133 WORD
134 DosGetCountryInfoEx(IN BYTE InfoId,
135 IN WORD CodePage,
136 IN WORD CountryId,
137 OUT PDOS_COUNTRY_INFO_2 CountryInfo,
138 IN OUT PWORD BufferSize)
139 {
140 // FIXME: use: CodePage; CountryId
141 // FIXME: Check BufferSize
142 // FIXME: Use NLSFUNC resident?
143
144 switch (InfoId)
145 {
146 /* Get General Internationalization Info (similar to AX=3800h) */
147 case 0x01:
148 {
149 WORD ErrorCode;
150 ErrorCode = DosGetCountryInfo(&CountryId,
151 &CountryInfo->CountryInfoEx.CountryInfo);
152 if (ErrorCode != ERROR_SUCCESS) return ErrorCode;
153 CountryInfo->CountryInfoEx.Size = sizeof(CountryInfo->CountryInfoEx);
154 CountryInfo->CountryInfoEx.CountryId = CountryId;
155 // CountryInfo->CodePage;
156 break;
157 }
158
159 /* Get Pointer to Uppercase Table */
160 case 0x02:
161 CountryInfo->UpCaseTblPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, UpCaseTbl), CountryDataSegment);
162 break;
163
164 /* Get Pointer to Lowercase Table */
165 case 0x03:
166 CountryInfo->LoCaseTblPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, LoCaseTbl), CountryDataSegment);
167 break;
168
169 /* Get Pointer to Filename Uppercase Table */
170 case 0x04:
171 CountryInfo->FNameUpCaseTblPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, UpCaseTbl), CountryDataSegment);
172 break;
173
174 /* Get Pointer to Filename Terminator Table */
175 case 0x05:
176 CountryInfo->FNameTermTblPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, FNameTermTbl), CountryDataSegment);
177 break;
178
179 /* Get Pointer to Collating Sequence Table */
180 case 0x06:
181 CountryInfo->CollateTblPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, CollateTbl), CountryDataSegment);
182 break;
183
184 /* Get Pointer to Double-Byte Character Set Table */
185 case 0x07:
186 CountryInfo->DBCSLeadTblPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, DBCSLeadTbl), CountryDataSegment);
187 break;
188
189 default:
190 return ERROR_CALL_NOT_IMPLEMENTED;
191 }
192 CountryInfo->InfoId = InfoId;
193
194 return ERROR_SUCCESS;
195 }
196
197 WORD DosIfCharYesNo(WORD Char)
198 {
199 Char = toupper(Char);
200
201 /* NO-type */
202 if (Char == YesNoTable[1])
203 return 0x0000;
204 /* YES-type */
205 if (Char == YesNoTable[0])
206 return 0x0001;
207 /* Unknown type */
208 return 0x0002;
209 }
210
211 CHAR DosToUpper(CHAR Char)
212 {
213 // FIXME: Use the current locale
214 return toupper(Char);
215 }
216
217 VOID DosToUpperStrN(PCHAR DestStr, PCHAR SrcStr, WORD Length)
218 {
219 while (Length-- > 0)
220 *DestStr++ = toupper(*SrcStr++);
221 }
222
223 VOID DosToUpperStrZ(PSTR DestStr, PSTR SrcStr)
224 {
225 while (*SrcStr)
226 *DestStr++ = toupper(*SrcStr++);
227 }
228
229 BOOLEAN DosCountryInitialize(VOID)
230 {
231 UINT i;
232
233 /* Initialize some memory to store country information */
234 // FIXME: Can we use instead some static area from the DOS data structure??
235 CountryDataSegment = DosAllocateMemory(sizeof(COUNTRY_DATA), NULL);
236 if (CountryDataSegment == 0) return FALSE;
237 CountryData = (PCOUNTRY_DATA)SEG_OFF_TO_PTR(CountryDataSegment, 0x0000);
238
239 RtlMoveMemory(CountryData->CaseMapRoutine,
240 CaseMapRoutine,
241 sizeof(CaseMapRoutine));
242
243 CountryData->UpCaseTbl.Size = ARRAYSIZE(CountryData->UpCaseTbl.Data);
244 for (i = 0; i < CountryData->UpCaseTbl.Size; ++i)
245 CountryData->UpCaseTbl.Data[i] = 0x80 + i;
246
247 CountryData->LoCaseTbl.Size = ARRAYSIZE(CountryData->LoCaseTbl.Data);
248 for (i = 0; i < CountryData->LoCaseTbl.Size; ++i)
249 CountryData->LoCaseTbl.Data[i] = i;
250
251 CountryData->FNameTermTbl.Size = ARRAYSIZE(CountryData->FNameTermTbl.Data);
252 CountryData->FNameTermTbl.Data[ 0] = 0x01; // Dummy Byte
253 CountryData->FNameTermTbl.Data[ 1] = 0x00; // Lowest permissible Character Value for Filename
254 CountryData->FNameTermTbl.Data[ 2] = 0xFF; // Highest permissible Character Value for Filename
255 CountryData->FNameTermTbl.Data[ 3] = 0x00; // Dummy Byte
256 CountryData->FNameTermTbl.Data[ 4] = 0x00; // First excluded Character in Range \ all characters in this
257 CountryData->FNameTermTbl.Data[ 5] = 0x20; // Last excluded Character in Range / range are illegal
258 CountryData->FNameTermTbl.Data[ 6] = 0x02; // Dummy Byte
259 CountryData->FNameTermTbl.Data[ 7] = 14; // Number of illegal (terminator) Characters
260 // CountryData->FNameTermTbl.Data[ 8] = ".\"/\\[]:|<>+=;,"; // Characters which terminate a Filename
261 CountryData->FNameTermTbl.Data[ 8] = '.';
262 CountryData->FNameTermTbl.Data[ 9] = '\"';
263 CountryData->FNameTermTbl.Data[10] = '/';
264 CountryData->FNameTermTbl.Data[11] = '\\';
265 CountryData->FNameTermTbl.Data[12] = '[';
266 CountryData->FNameTermTbl.Data[13] = ']';
267 CountryData->FNameTermTbl.Data[14] = ':';
268 CountryData->FNameTermTbl.Data[15] = '|';
269 CountryData->FNameTermTbl.Data[16] = '<';
270 CountryData->FNameTermTbl.Data[17] = '>';
271 CountryData->FNameTermTbl.Data[18] = '+';
272 CountryData->FNameTermTbl.Data[19] = '=';
273 CountryData->FNameTermTbl.Data[20] = ';';
274 CountryData->FNameTermTbl.Data[21] = ',';
275
276 CountryData->CollateTbl.Size = ARRAYSIZE(CountryData->CollateTbl.Data);
277 for (i = 0; i < CountryData->CollateTbl.Size; ++i)
278 CountryData->CollateTbl.Data[i] = i;
279
280 CountryData->DBCSLeadTbl.Size = 0; // Empty DBCS table
281 CountryData->DBCSLeadTbl.Data[0] = 0x0000;
282
283 return TRUE;
284 }