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