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)
8 * NOTE: Support for default (english) language only.
9 * For other languages, please use COUNTRY.SYS
17 /* PRIVATE VARIABLES **********************************************************/
19 /* CaseMap routine: should call INT 65h, AL=20h */
20 // ATM, just do nothing.
21 static const BYTE CaseMapRoutine
[] =
28 #define DATATABLE(name, type, len) \
29 typedef struct _##name \
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);
41 typedef struct _COUNTRY_DATA
43 BYTE CaseMapRoutine
[sizeof(CaseMapRoutine
)];
44 UPPERCASE UpCaseTbl
; // Used also for filename uppercase
46 FNAMETERM FNameTermTbl
;
49 } COUNTRY_DATA
, *PCOUNTRY_DATA
;
53 /* Global data contained in guest memory */
54 static WORD CountryDataSegment
;
55 static PCOUNTRY_DATA CountryData
;
57 WORD YesNoTable
[2] = { MAKEWORD('Y', 0), MAKEWORD('N', 0) };
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
65 /* PRIVATE FUNCTIONS **********************************************************/
67 /* PUBLIC FUNCTIONS ***********************************************************/
70 DosGetCountryInfo(IN OUT PWORD CountryId
,
71 OUT PDOS_COUNTRY_INFO CountryInfo
)
76 Return
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_IDATE
| LOCALE_RETURN_NUMBER
, // LOCALE_ILDATE | LOCALE_RETURN_NUMBER
79 if (Return
== 0) return LOWORD(GetLastError());
80 CountryInfo
->DateTimeFormat
= (WORD
)NumVal
;
82 Return
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_SCURRENCY
,
83 (LPSTR
)&CountryInfo
->CurrencySymbol
,
84 sizeof(CountryInfo
->CurrencySymbol
));
85 if (Return
== 0) return LOWORD(GetLastError());
87 Return
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_STHOUSAND
,
88 (LPSTR
)&CountryInfo
->ThousandSep
,
89 sizeof(CountryInfo
->ThousandSep
));
90 if (Return
== 0) return LOWORD(GetLastError());
92 Return
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_SDECIMAL
,
93 (LPSTR
)&CountryInfo
->DecimalSep
,
94 sizeof(CountryInfo
->DecimalSep
));
95 if (Return
== 0) return LOWORD(GetLastError());
97 Return
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_SDATE
,
98 (LPSTR
)&CountryInfo
->DateSep
,
99 sizeof(CountryInfo
->DateSep
));
100 if (Return
== 0) return LOWORD(GetLastError());
102 Return
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_STIME
,
103 (LPSTR
)&CountryInfo
->TimeSep
,
104 sizeof(CountryInfo
->TimeSep
));
105 if (Return
== 0) return LOWORD(GetLastError());
107 // NOTE: '4: Symbol replace decimal separator' is unsupported.
108 Return
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_ICURRENCY
| LOCALE_RETURN_NUMBER
,
111 if (Return
== 0) return LOWORD(GetLastError());
112 CountryInfo
->CurrencyFormat
= (BYTE
)NumVal
;
114 Return
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_ICURRDIGITS
| LOCALE_RETURN_NUMBER
, // LOCALE_IDIGITS | LOCALE_RETURN_NUMBER
117 if (Return
== 0) return LOWORD(GetLastError());
118 CountryInfo
->CurrencyDigitsNum
= (BYTE
)NumVal
;
120 Return
= GetLocaleInfoA(LOCALE_USER_DEFAULT
, LOCALE_ITIME
| LOCALE_RETURN_NUMBER
,
123 if (Return
== 0) return LOWORD(GetLastError());
124 CountryInfo
->TimeFormat
= (BYTE
)NumVal
;
126 CountryInfo
->CaseMapPtr
= MAKELONG(FIELD_OFFSET(COUNTRY_DATA
, CaseMapRoutine
), CountryDataSegment
);
128 // CountryInfo->DataListSep;
130 return ERROR_SUCCESS
;
134 DosGetCountryInfoEx(IN BYTE InfoId
,
137 OUT PDOS_COUNTRY_INFO_2 CountryInfo
,
138 IN OUT PWORD BufferSize
)
140 // FIXME: use: CodePage; CountryId
141 // FIXME: Check BufferSize
142 // FIXME: Use NLSFUNC resident?
146 /* Get General Internationalization Info (similar to AX=3800h) */
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;
159 /* Get Pointer to Uppercase Table */
161 CountryInfo
->UpCaseTblPtr
= MAKELONG(FIELD_OFFSET(COUNTRY_DATA
, UpCaseTbl
), CountryDataSegment
);
164 /* Get Pointer to Lowercase Table */
166 CountryInfo
->LoCaseTblPtr
= MAKELONG(FIELD_OFFSET(COUNTRY_DATA
, LoCaseTbl
), CountryDataSegment
);
169 /* Get Pointer to Filename Uppercase Table */
171 CountryInfo
->FNameUpCaseTblPtr
= MAKELONG(FIELD_OFFSET(COUNTRY_DATA
, UpCaseTbl
), CountryDataSegment
);
174 /* Get Pointer to Filename Terminator Table */
176 CountryInfo
->FNameTermTblPtr
= MAKELONG(FIELD_OFFSET(COUNTRY_DATA
, FNameTermTbl
), CountryDataSegment
);
179 /* Get Pointer to Collating Sequence Table */
181 CountryInfo
->CollateTblPtr
= MAKELONG(FIELD_OFFSET(COUNTRY_DATA
, CollateTbl
), CountryDataSegment
);
184 /* Get Pointer to Double-Byte Character Set Table */
186 CountryInfo
->DBCSLeadTblPtr
= MAKELONG(FIELD_OFFSET(COUNTRY_DATA
, DBCSLeadTbl
), CountryDataSegment
);
190 return ERROR_CALL_NOT_IMPLEMENTED
;
192 CountryInfo
->InfoId
= InfoId
;
194 return ERROR_SUCCESS
;
197 WORD
DosIfCharYesNo(WORD Char
)
199 Char
= toupper(Char
);
202 if (Char
== YesNoTable
[1])
205 if (Char
== YesNoTable
[0])
211 CHAR
DosToUpper(CHAR Char
)
213 // FIXME: Use the current locale
214 return toupper(Char
);
217 VOID
DosToUpperStrN(PCHAR DestStr
, PCHAR SrcStr
, WORD Length
)
220 *DestStr
++ = toupper(*SrcStr
++);
223 VOID
DosToUpperStrZ(PSTR DestStr
, PSTR SrcStr
)
226 *DestStr
++ = toupper(*SrcStr
++);
229 BOOLEAN
DosCountryInitialize(VOID
)
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);
239 RtlMoveMemory(CountryData
->CaseMapRoutine
,
241 sizeof(CaseMapRoutine
));
243 CountryData
->UpCaseTbl
.Size
= ARRAYSIZE(CountryData
->UpCaseTbl
.Data
);
244 for (i
= 0; i
< CountryData
->UpCaseTbl
.Size
; ++i
)
245 CountryData
->UpCaseTbl
.Data
[i
] = 0x80 + i
;
247 CountryData
->LoCaseTbl
.Size
= ARRAYSIZE(CountryData
->LoCaseTbl
.Data
);
248 for (i
= 0; i
< CountryData
->LoCaseTbl
.Size
; ++i
)
249 CountryData
->LoCaseTbl
.Data
[i
] = i
;
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] = ',';
276 CountryData
->CollateTbl
.Size
= ARRAYSIZE(CountryData
->CollateTbl
.Data
);
277 for (i
= 0; i
< CountryData
->CollateTbl
.Size
; ++i
)
278 CountryData
->CollateTbl
.Data
[i
] = i
;
280 CountryData
->DBCSLeadTbl
.Size
= 0; // Empty DBCS table
281 CountryData
->DBCSLeadTbl
.Data
[0] = 0x0000;