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 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 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 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 218 CHAR DosToUpper(CHAR Char) 219 { 220 // FIXME: Use the current locale 221 return toupper(Char); 222 } 223 224 VOID DosToUpperStrN(PCHAR DestStr, PCHAR SrcStr, WORD Length) 225 { 226 while (Length-- > 0) 227 *DestStr++ = toupper(*SrcStr++); 228 } 229 230 VOID DosToUpperStrZ(PSTR DestStr, PSTR SrcStr) 231 { 232 while (*SrcStr) 233 *DestStr++ = toupper(*SrcStr++); 234 } 235 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