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