Cleanup country support, add historic locales
This commit is contained in:
parent
5954fa1c45
commit
6c2badb7ca
16 changed files with 3189 additions and 938 deletions
|
@ -31,7 +31,7 @@ _dosbox()
|
|||
)\
|
||||
_exclusive_opts=(
|
||||
--version --printconf --editconf -resetconf -eraseconf
|
||||
-erasemapper -resetmapper --list-glshaders
|
||||
-erasemapper -resetmapper --list-countries --list-glshaders
|
||||
)
|
||||
####
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH DOSBOX 1 "Oct 27, 2023"
|
||||
.TH DOSBOX 1 "Dec 4, 2023"
|
||||
.\" Please adjust this date whenever revising the manpage.
|
||||
|
||||
.SH NAME
|
||||
|
@ -39,6 +39,8 @@ dosbox \- DOSBox Staging, x86/DOS emulator, a modern continuation of DOSBox
|
|||
.LP
|
||||
.B dosbox \-\-working-dir <path>
|
||||
.LP
|
||||
.B dosbox \-\-list\-countries
|
||||
.LP
|
||||
.B dosbox \-\-list\-glshaders
|
||||
.LP
|
||||
.B dosbox \-\-erasemapper
|
||||
|
@ -89,8 +91,11 @@ Legacy single dash abbrevations still work for backwards compatibility.
|
|||
--working-dir <path> Set working directory to <path>. DOSBox will act as if
|
||||
started from this directory.
|
||||
|
||||
--list-countries List all supported countries with their numeric codes.
|
||||
Codes are to be used in the 'country' config setting.
|
||||
|
||||
--list-glshaders List all available OpenGL shaders and their paths.
|
||||
Results are useable in the 'glshader = ' config setting.
|
||||
Shaders are to be used in the 'glshader' config setting.
|
||||
|
||||
--fullscreen Start in fullscreen mode.
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ struct CommandLineArguments {
|
|||
bool noprimaryconf;
|
||||
bool nolocalconf;
|
||||
bool fullscreen;
|
||||
bool list_countries;
|
||||
bool list_glshaders;
|
||||
bool version;
|
||||
bool help;
|
||||
|
|
|
@ -159,4 +159,27 @@ bool get_expanded_files(const std::string &path,
|
|||
|
||||
std::string get_language_from_os();
|
||||
|
||||
// A printf variant outputting UTF-8 strings
|
||||
template <typename... Arguments>
|
||||
void printf_utf8(const char* format, Arguments... arguments)
|
||||
{
|
||||
#ifdef WIN32
|
||||
constexpr uint16_t CodePageUtf8 = 65001;
|
||||
|
||||
const auto old_code_page = GetConsoleOutputCP();
|
||||
SetConsoleOutputCP(CodePageUtf8);
|
||||
try {
|
||||
printf(format, arguments...);
|
||||
} catch (...) {
|
||||
SetConsoleOutputCP(old_code_page);
|
||||
throw;
|
||||
}
|
||||
|
||||
SetConsoleOutputCP(old_code_page);
|
||||
#else
|
||||
// Assume any OS without special support uses UTF-8 as console encoding
|
||||
printf(format, arguments...);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -878,127 +878,36 @@ static inline uint8_t RealHandle(uint16_t handle) {
|
|||
return psp.GetFileHandle(handle);
|
||||
}
|
||||
|
||||
#define DOS_DATE_FORMAT_OFS 0
|
||||
#define DOS_DATE_SEPARATOR_OFS 11
|
||||
#define DOS_TIME_FORMAT_OFS 17
|
||||
#define DOS_TIME_SEPARATOR_OFS 13
|
||||
#define DOS_THOUSANDS_SEPARATOR_OFS 7
|
||||
#define DOS_DECIMAL_SEPARATOR_OFS 9
|
||||
/* Locale information */
|
||||
|
||||
// Sources of the country numbers:
|
||||
// - MS-DOS 6.22, COUNTRY.TXT file
|
||||
// - PC-DOS 2000, HELP COUNTRY command, information table
|
||||
// - DR-DOS 7.03, HELP, Table 9-2: Country Codes and Code Pages
|
||||
// - FreeDOS 1.3, country.asm (source code)
|
||||
// - Paragon PTS DOS 2000 Pro manual
|
||||
// - https://en.wikipedia.org/wiki/List_of_country_calling_codes
|
||||
// (used for remaining countries, especially where we have keyboard layout)
|
||||
enum class Country : uint16_t {
|
||||
United_States = 1, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Canada_French = 2, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Latin_America = 3, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Canada_English = 4, // MS-DOS
|
||||
Russia = 7, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
South_Africa = 27, // MS-DOS
|
||||
Greece = 30, // MS-DOS, PC-DOS, FreeDOS
|
||||
Netherlands = 31, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Belgium = 32, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
France = 33, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Spain = 34, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Hungary = 36, // MS-DOS, PC-DOS, DR-DOS, FreeDOS
|
||||
Yugoslavia = 38, // MS-DOS, PC-DOS, FreeDOS
|
||||
Italy = 39, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Romania = 40, // MS-DOS, PC-DOS, FreeDOS
|
||||
Switzerland = 41, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Czechia = 42, // MS-DOS (*)
|
||||
Austria = 43, // MS-DOS FreeDOS
|
||||
United_Kingdom = 44, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Denmark = 45, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Sweden = 46, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Norway = 47, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Poland = 48, // MS-DOS, PC-DOS, FreeDOS
|
||||
Germany = 49, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Mexico = 52, // MS-DOS
|
||||
Argentina = 54, // MS-DOS, FreeDOS
|
||||
Brazil = 55, // MS-DOS, PC-DOS, FreeDOS
|
||||
Chile = 56, // MS-DOS
|
||||
Colombia = 57, // MS-DOS
|
||||
Venezuela = 58, // MS-DOS
|
||||
Malaysia = 60, // MS-DOS, FreeDOS
|
||||
Australia = 61, // MS-DOS, PC-DOS, DR-DOS, FreeDOS (*)
|
||||
Philippines = 63,
|
||||
New_Zealand = 64, // MS-DOS
|
||||
Singapore = 65, // MS-DOS, FreeDOS
|
||||
Kazakhstan = 77,
|
||||
Japan = 81, // MS-DOS, PC-DOS, FreeDOS, Paragon
|
||||
Korea = 82, // MS-DOS, FreeDOS, Paragon (*)
|
||||
Vietnam = 84,
|
||||
China = 86, // MS-DOS, FreeDOS, Paragon
|
||||
Turkey = 90, // MS-DOS, PC-DOS, DR-DOS, FreeDOS
|
||||
India = 91, // MS-DOS, FreeDOS
|
||||
Niger = 227,
|
||||
Benin = 229,
|
||||
Nigeria = 234,
|
||||
Faroe_Islands = 298,
|
||||
Portugal = 351, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Ireland = 353, // MS-DOS
|
||||
Iceland = 354, // MS-DOS, PC-DOS
|
||||
Albania = 355, // MS-DOS, PC-DOS
|
||||
Malta = 356,
|
||||
Finland = 358, // MS-DOS, PC-DOS, DR-DOS, FreeDOS, Paragon
|
||||
Bulgaria = 359, // MS-DOS, PC-DOS, FreeDOS
|
||||
Lithuania = 370,
|
||||
Latvia = 371,
|
||||
Estonia = 372,
|
||||
Armenia = 374,
|
||||
Belarus = 375, // FreeDOS
|
||||
Ukraine = 380, // FreeDOS
|
||||
Serbia = 381, // MS-DOS, PC-DOS, FreeDOS (*)
|
||||
Montenegro = 382,
|
||||
Croatia = 384, // MS-DOS, FreeDOS (*)
|
||||
Slovenia = 386, // MS-DOS, PC-DOS, FreeDOS
|
||||
Bosnia_Herzegovina = 387, // PC-DOS, FreeDOS
|
||||
Macedonia = 389, // MS-DOS, PC-DOS, FreeDOS
|
||||
Slovakia = 421, // MS-DOS (*)
|
||||
Ecuador = 593, // MS-DOS
|
||||
Arabic = 785, // MS-DOS, FreeDOS, Paragon (*)
|
||||
Hong_Kong = 852, // MS-DOS
|
||||
Taiwan = 886, // MS-DOS
|
||||
Israel = 972, // MS-DOS, FreeDOS, Paragon
|
||||
Mongolia = 976,
|
||||
Tadjikistan = 992,
|
||||
Turkmenistan = 993,
|
||||
Azerbaijan = 994,
|
||||
Georgia = 995,
|
||||
Kyrgyzstan = 996,
|
||||
Uzbekistan = 998,
|
||||
|
||||
// (*) Remarks:
|
||||
// - MS-DOS and PC-DOS use country code 381 for both Serbia and Montenegro
|
||||
// - MS-DOS and PC-DOS use country code 61 also for International English
|
||||
// - PC-DOS uses country code 381 also for Yugoslavia Cyrillic
|
||||
// - PC-DOS uses country code 385 (not 386) for Croatia
|
||||
// - PC-DOS uses country code 388 for Bosna/Herzegovina Cyrillic
|
||||
// - PC-DOS uses country code 421 for Czechia and 422 for Slovakia
|
||||
// - FreeDOS uses country code 042 for Czechoslovakia
|
||||
// - FreeDOS calls country code 785 Middle-East,
|
||||
// MS-DOS calls it Arabic South
|
||||
// - Paragon PTS DOS uses country code 61 only for Australia
|
||||
// - Paragon PTS DOS uses country code 88 for Taiwan
|
||||
// - DOSes use country code 82 for Korea, despite country calling code
|
||||
// 82 is assigned to South Korea
|
||||
|
||||
// FreeDOS also supports the following, not yet handled here:
|
||||
// - Belgium/Dutch 40032
|
||||
// - Belgium/French 41032
|
||||
// - Belgium/German 42032
|
||||
// - Spain/Spanish 40034
|
||||
// - Spain/Catalan 41034
|
||||
// - Spain/Gallegan 42034
|
||||
// - Spain/Basque 43034
|
||||
// - Switzerland/German 40041
|
||||
// - Switzerland/French 41041
|
||||
// - Switzerland/Italian 42041
|
||||
enum class DosDateFormat : uint8_t {
|
||||
MonthDayYear = 0,
|
||||
DayMonthYear = 1,
|
||||
YearMonthDay = 2,
|
||||
};
|
||||
|
||||
enum class DosTimeFormat : uint8_t {
|
||||
Time12H = 0, // AM/PM
|
||||
Time24H = 1,
|
||||
};
|
||||
|
||||
enum class DosCurrencyFormat : uint8_t {
|
||||
SymbolAmount = 0,
|
||||
AmountSymbol = 1,
|
||||
SymbolSpaceAmount = 2,
|
||||
AmountSpaceSymbol = 3,
|
||||
|
||||
// Some sources claim that bit 2 set means currency symbol should
|
||||
// replace decimal point; so far it is unknown which (if any)
|
||||
// COUNTRY.SYS uses this bit, most likely no DOS software uses it.
|
||||
};
|
||||
|
||||
DosDateFormat DOS_GetLocaleDateFormat();
|
||||
DosTimeFormat DOS_GetLocaleTimeFormat();
|
||||
char DOS_GetLocaleDateSeparator();
|
||||
char DOS_GetLocaleTimeSeparator();
|
||||
char DOS_GetLocaleThousandsSeparator();
|
||||
char DOS_GetLocaleDecimalSeparator();
|
||||
char DOS_GetLocaleListSeparator();
|
||||
|
||||
#endif
|
||||
|
|
167
src/dos/dos.cpp
167
src/dos/dos.cpp
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "bios.h"
|
||||
#include "callback.h"
|
||||
#include "dos_locale.h"
|
||||
#include "drives.h"
|
||||
#include "mem.h"
|
||||
#include "program_mount_common.h"
|
||||
|
@ -52,146 +53,6 @@ void DOS_SetError(uint16_t code) {
|
|||
dos.errorcode=code;
|
||||
}
|
||||
|
||||
typedef struct CountryInfo {
|
||||
Country country_number;
|
||||
uint8_t date_format;
|
||||
uint8_t date_separator;
|
||||
uint8_t time_format;
|
||||
uint8_t time_separator;
|
||||
uint8_t thousands_separator;
|
||||
uint8_t decimal_separator;
|
||||
} CountryInfo;
|
||||
|
||||
static const CountryInfo& LookupCountryInfo(const uint16_t country_number) {
|
||||
|
||||
static constexpr uint8_t DATE_MDY = 0;
|
||||
static constexpr uint8_t DATE_DMY = 1;
|
||||
static constexpr uint8_t DATE_YMD = 2;
|
||||
|
||||
static constexpr uint8_t TIME_12H = 0;
|
||||
static constexpr uint8_t TIME_24H = 1;
|
||||
|
||||
static constexpr uint8_t SEP_SPACE = 0x20; // ( )
|
||||
static constexpr uint8_t SEP_APOST = 0x27; // (')
|
||||
static constexpr uint8_t SEP_COMMA = 0x2c; // (,)
|
||||
static constexpr uint8_t SEP_DASH = 0x2d; // (-)
|
||||
static constexpr uint8_t SEP_PERIOD = 0x2e; // (.)
|
||||
static constexpr uint8_t SEP_SLASH = 0x2f; // (/)
|
||||
static constexpr uint8_t SEP_COLON = 0x3a; // (:)
|
||||
|
||||
// Values here reflect the current KDE/Linux system settings - they will probably not produce 100% same
|
||||
// result as old MS-DOS systems, but should at least provide reasonably consistent user experience with
|
||||
// certain host operating systems.
|
||||
static constexpr CountryInfo COUNTRY_INFO[]= {
|
||||
// | Date fmt | Date separ | Time fmt | Time separ | 1000 separ | Dec separ |
|
||||
// { Country::International , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_PERIOD }, // C
|
||||
{ Country::United_States , DATE_MDY , SEP_SLASH , TIME_12H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // en_US
|
||||
{ Country::Canada_French , DATE_YMD , SEP_DASH , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // fr_CA
|
||||
{ Country::Latin_America , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // es_419
|
||||
{ Country::Canada_English , DATE_YMD , SEP_DASH , TIME_24H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // en_CA
|
||||
{ Country::Russia , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // ru_RU
|
||||
{ Country::South_Africa , DATE_YMD , SEP_DASH , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // af_ZA
|
||||
{ Country::Greece , DATE_DMY , SEP_SLASH , TIME_12H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // el_GR
|
||||
{ Country::Netherlands , DATE_DMY , SEP_DASH , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // nl_NL
|
||||
{ Country::Belgium , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // fr_BE
|
||||
{ Country::France , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // fr_FR
|
||||
{ Country::Spain , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // es_ES
|
||||
{ Country::Hungary , DATE_YMD , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // hu_HU
|
||||
{ Country::Yugoslavia , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // sr_RS/sr_ME/hr_HR/sk_SK/bs_BA/mk_MK
|
||||
{ Country::Italy , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // it_IT
|
||||
{ Country::Romania , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // ro_RO
|
||||
{ Country::Switzerland , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_APOST , SEP_PERIOD }, // ??_CH
|
||||
{ Country::Czechia , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // cs_CZ
|
||||
{ Country::Austria , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // de_AT
|
||||
{ Country::United_Kingdom , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // en_GB
|
||||
// | Date fmt | Date separ | Time fmt | Time separ | 1000 separ | Dec separ |
|
||||
{ Country::Denmark , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // da_DK
|
||||
{ Country::Sweden , DATE_YMD , SEP_DASH , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // sv_SE
|
||||
{ Country::Norway , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // nn_NO
|
||||
{ Country::Poland , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // pl_PL
|
||||
{ Country::Germany , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // de_DE
|
||||
{ Country::Argentina , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // es_AR
|
||||
{ Country::Brazil , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // pt_BR
|
||||
{ Country::Chile , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // es_CL
|
||||
{ Country::Colombia , DATE_DMY , SEP_SLASH , TIME_12H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // es_CO
|
||||
{ Country::Venezuela , DATE_DMY , SEP_SLASH , TIME_12H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // es_VE
|
||||
{ Country::Malaysia , DATE_DMY , SEP_SLASH , TIME_12H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // ms_MY
|
||||
{ Country::Australia , DATE_DMY , SEP_SLASH , TIME_12H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // en_AU
|
||||
{ Country::Philippines , DATE_DMY , SEP_SLASH , TIME_12H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // fil_PH
|
||||
{ Country::New_Zealand , DATE_DMY , SEP_SLASH , TIME_12H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // en_NZ
|
||||
{ Country::Singapore , DATE_DMY , SEP_SLASH , TIME_12H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // ms_SG
|
||||
{ Country::Kazakhstan , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // kk_KZ
|
||||
{ Country::Japan , DATE_YMD , SEP_SLASH , TIME_24H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // ja_JP
|
||||
{ Country::Korea , DATE_YMD , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // ko_KR
|
||||
{ Country::Vietnam , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // vi_VN
|
||||
{ Country::China , DATE_YMD , SEP_SLASH , TIME_24H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // zh_CN
|
||||
// | Date fmt | Date separ | Time fmt | Time separ | 1000 separ | Dec separ
|
||||
{ Country::Turkey , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // tr_TR
|
||||
{ Country::India , DATE_DMY , SEP_SLASH , TIME_12H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // hi_IN
|
||||
{ Country::Niger , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // fr_NE
|
||||
{ Country::Benin , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // fr_BJ
|
||||
{ Country::Nigeria , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // en_NG
|
||||
{ Country::Faroe_Islands , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // fo_FO
|
||||
{ Country::Portugal , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // pt_PT
|
||||
{ Country::Ireland , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // en_IE
|
||||
{ Country::Iceland , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // is_IS
|
||||
{ Country::Albania , DATE_DMY , SEP_PERIOD , TIME_12H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // sq_AL
|
||||
{ Country::Malta , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // mt_MT
|
||||
{ Country::Finland , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // fi_FI
|
||||
{ Country::Bulgaria , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // bg_BG
|
||||
{ Country::Lithuania , DATE_YMD , SEP_DASH , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // lt_LT
|
||||
{ Country::Latvia , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // lv_LV
|
||||
{ Country::Estonia , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // et_EE
|
||||
{ Country::Armenia , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // hy_AM
|
||||
{ Country::Belarus , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // be_BY
|
||||
{ Country::Ukraine , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // uk_UA
|
||||
{ Country::Serbia , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // sr_RS
|
||||
// | Date fmt | Date separ | Time fmt | Time separ | 1000 separ | Dec separ |
|
||||
{ Country::Montenegro , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // sr_ME
|
||||
{ Country::Croatia , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // hr_HR
|
||||
{ Country::Slovenia , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // sl_SI
|
||||
{ Country::Bosnia_Herzegovina , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // bs_BA
|
||||
{ Country::Macedonia , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // mk_MK
|
||||
{ Country::Slovakia , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // sk_SK
|
||||
{ Country::Ecuador , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // es_EC
|
||||
{ Country::Arabic , DATE_DMY , SEP_PERIOD , TIME_12H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // ar_??
|
||||
{ Country::Hong_Kong , DATE_DMY , SEP_SLASH , TIME_12H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // en_HK, zh_HK
|
||||
{ Country::Taiwan , DATE_YMD , SEP_SLASH , TIME_24H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // zh_TW
|
||||
{ Country::Israel , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // he_IL
|
||||
{ Country::Mongolia , DATE_YMD , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_COMMA , SEP_PERIOD }, // mn_MN
|
||||
{ Country::Tadjikistan , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // tg_TJ
|
||||
{ Country::Turkmenistan , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // tk_TM
|
||||
{ Country::Azerbaijan , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_PERIOD , SEP_COMMA }, // az_AZ
|
||||
{ Country::Georgia , DATE_DMY , SEP_PERIOD , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // ka_GE
|
||||
{ Country::Kyrgyzstan , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // ky_KG
|
||||
{ Country::Uzbekistan , DATE_DMY , SEP_SLASH , TIME_24H , SEP_COLON , SEP_SPACE , SEP_COMMA }, // uz_UZ
|
||||
// | Date fmt | Date separ | Time fmt | Time separ | 1000 separ | Dec separ |
|
||||
};
|
||||
|
||||
for (const auto& country : COUNTRY_INFO) {
|
||||
if (static_cast<uint16_t>(country.country_number) == country_number)
|
||||
return country;
|
||||
}
|
||||
|
||||
LOG_WARNING("DOS: No locale info for country %d", country_number);
|
||||
return COUNTRY_INFO[0];
|
||||
}
|
||||
|
||||
void DOS_SetCountry(uint16_t country_number)
|
||||
{
|
||||
if (dos.tables.country == nullptr)
|
||||
return;
|
||||
|
||||
const auto country_info = LookupCountryInfo(country_number);
|
||||
|
||||
dos.tables.country[DOS_DATE_FORMAT_OFS] = country_info.date_format;
|
||||
dos.tables.country[DOS_DATE_SEPARATOR_OFS] = country_info.date_separator;
|
||||
dos.tables.country[DOS_TIME_FORMAT_OFS] = country_info.time_format;
|
||||
dos.tables.country[DOS_TIME_SEPARATOR_OFS] = country_info.time_separator;
|
||||
dos.tables.country[DOS_THOUSANDS_SEPARATOR_OFS] = country_info.thousands_separator;
|
||||
dos.tables.country[DOS_DECIMAL_SEPARATOR_OFS] = country_info.decimal_separator;
|
||||
}
|
||||
|
||||
uint16_t DOS_GetBiosTimePacked()
|
||||
{
|
||||
const auto ticks = mem_readd(BIOS_TIMER);
|
||||
|
@ -793,16 +654,26 @@ static Bitu DOS_21Handler(void) {
|
|||
};
|
||||
LOG(LOG_MISC,LOG_ERROR)("DOS:0x37:Call for not supported switchchar");
|
||||
break;
|
||||
case 0x38: /* Set Country Code */
|
||||
if (reg_al == 0) { /* Get country specific information */
|
||||
case 0x38:
|
||||
if (reg_dx == 0xffff) { /* Set Country Code */
|
||||
// TODO: For unknown reason on modern DOSes (checked
|
||||
// MS-DOS 6.22, PC DOS 2000 and DR DOS 7.03) this only
|
||||
// works when setting the country to the one currently
|
||||
// set - unknown, why
|
||||
countryNo = (reg_al == 0xff) ? reg_bx : reg_al;
|
||||
if (DOS_SetCountry(countryNo)) {
|
||||
reg_ax = 0;
|
||||
CALLBACK_SCF(false);
|
||||
} else {
|
||||
reg_ax = 0x02; // invalid country
|
||||
CALLBACK_SCF(true);
|
||||
}
|
||||
} else { /* Get country specific information */
|
||||
PhysPt dest = SegPhys(ds) + reg_dx;
|
||||
MEM_BlockWrite(dest, dos.tables.country, 0x18);
|
||||
reg_ax = reg_bx = 0x01;
|
||||
CALLBACK_SCF(false);
|
||||
} else { /* Set country code */
|
||||
countryNo = reg_al == 0xff ? reg_bx : reg_al;
|
||||
DOS_SetCountry(countryNo);
|
||||
reg_ax = 0;
|
||||
reg_bx = DOS_GetCountry();
|
||||
reg_ah = 0;
|
||||
reg_al = reg_bl;
|
||||
CALLBACK_SCF(false);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -30,6 +30,7 @@ using sv = std::string_view;
|
|||
#include "bios_disk.h"
|
||||
#include "callback.h"
|
||||
#include "dos_inc.h"
|
||||
#include "dos_locale.h"
|
||||
#include "drives.h"
|
||||
#include "mapper.h"
|
||||
#include "math_utils.h"
|
||||
|
@ -37,10 +38,6 @@ using sv = std::string_view;
|
|||
#include "setup.h"
|
||||
#include "string_utils.h"
|
||||
|
||||
// The default codepage for DOS
|
||||
constexpr uint16_t default_cp_437 = 437;
|
||||
constexpr auto default_country = Country::United_States;
|
||||
|
||||
// A common pattern in the keyboard layout file is to try opening the requested
|
||||
// file first within DOS, then from the local path, and finally from builtin
|
||||
// resources. This function performs those in order and returns the first hit.
|
||||
|
@ -181,6 +178,12 @@ void KeyboardLayout::ReadKeyboardFile(const int32_t specific_layout)
|
|||
dos.loaded_codepage);
|
||||
}
|
||||
|
||||
static void log_layout_read_error()
|
||||
{
|
||||
LOG_WARNING("DOS: Error reading keyboard layout file: '%s'",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
static uint32_t read_kcl_file(const FILE_unique_ptr &kcl_file, const char *layout_id, bool first_id_only)
|
||||
{
|
||||
assert(kcl_file);
|
||||
|
@ -195,7 +198,7 @@ static uint32_t read_kcl_file(const FILE_unique_ptr &kcl_file, const char *layou
|
|||
|
||||
const auto seek_pos = 7 + rbuf[6];
|
||||
if (fseek(kcl_file.get(), seek_pos, SEEK_SET) != 0) {
|
||||
LOG_WARNING("LAYOUT: could not seek to byte %d in keyboard layout file: %s", seek_pos, strerror(errno));
|
||||
log_layout_read_error();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -214,10 +217,7 @@ static uint32_t read_kcl_file(const FILE_unique_ptr &kcl_file, const char *layou
|
|||
|
||||
constexpr int8_t lang_codes_offset = -2;
|
||||
if (fseek(kcl_file.get(), lang_codes_offset, SEEK_CUR) != 0) {
|
||||
LOG_ERR("LAYOUT: could not seek to the language codes "
|
||||
"at byte %d in keyboard layout: %s",
|
||||
check_cast<int>(cur_pos) + lang_codes_offset,
|
||||
strerror(errno));
|
||||
log_layout_read_error();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -252,9 +252,7 @@ static uint32_t read_kcl_file(const FILE_unique_ptr &kcl_file, const char *layou
|
|||
}
|
||||
}
|
||||
if (fseek(kcl_file.get(), cur_pos + 3 + len, SEEK_SET) != 0) {
|
||||
LOG_ERR("LAYOUT: could not seek to byte %d in keyboard layout file: %s",
|
||||
check_cast<int>(cur_pos) + 3 + len,
|
||||
strerror(errno));
|
||||
log_layout_read_error();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -319,8 +317,7 @@ KeyboardErrorCode KeyboardLayout::ReadKeyboardFile(const char *keyboard_file_nam
|
|||
if (tempfile) {
|
||||
const auto seek_pos = start_pos + 2;
|
||||
if (fseek(tempfile.get(), seek_pos, SEEK_SET) != 0) {
|
||||
LOG_WARNING("LAYOUT: could not seek to byte %d in keyboard layout file '%s': %s",
|
||||
seek_pos, keyboard_file_name, strerror(errno));
|
||||
log_layout_read_error();
|
||||
return KEYB_INVALIDFILE;
|
||||
}
|
||||
read_buf_size = (uint32_t)fread(read_buf, sizeof(uint8_t),
|
||||
|
@ -655,8 +652,9 @@ bool KeyboardLayout::SetMapKey(const uint8_t key, const uint16_t layouted_key,
|
|||
|
||||
uint16_t KeyboardLayout::ExtractCodePage(const char *keyboard_file_name)
|
||||
{
|
||||
if (!strcmp(keyboard_file_name, "none"))
|
||||
return default_cp_437;
|
||||
if (!strcmp(keyboard_file_name, "none")) {
|
||||
return DefaultCodePage437;
|
||||
}
|
||||
|
||||
size_t read_buf_size = 0;
|
||||
static uint8_t read_buf[65535];
|
||||
|
@ -670,7 +668,7 @@ uint16_t KeyboardLayout::ExtractCodePage(const char *keyboard_file_name)
|
|||
if (!load_builtin_keyboard_layouts(keyboard_file_name, tempfile, start_pos)) {
|
||||
LOG(LOG_BIOS, LOG_ERROR)
|
||||
("Keyboard layout file %s not found", keyboard_file_name);
|
||||
return default_cp_437;
|
||||
return DefaultCodePage437;
|
||||
}
|
||||
if (tempfile) {
|
||||
fseek(tempfile.get(), start_pos + 2, SEEK_SET);
|
||||
|
@ -684,7 +682,7 @@ uint16_t KeyboardLayout::ExtractCodePage(const char *keyboard_file_name)
|
|||
if ((dr<4) || (read_buf[0]!=0x4b) || (read_buf[1]!=0x4c) || (read_buf[2]!=0x46)) {
|
||||
LOG(LOG_BIOS, LOG_ERROR)
|
||||
("Invalid keyboard layout file %s", keyboard_file_name);
|
||||
return default_cp_437;
|
||||
return DefaultCodePage437;
|
||||
}
|
||||
|
||||
fseek(tempfile.get(), 0, SEEK_SET);
|
||||
|
@ -692,7 +690,7 @@ uint16_t KeyboardLayout::ExtractCodePage(const char *keyboard_file_name)
|
|||
}
|
||||
if (read_buf_size == 0) {
|
||||
LOG_WARNING("CODEPAGE: Could not read data from layout file %s", keyboard_file_name);
|
||||
return default_cp_437;
|
||||
return DefaultCodePage437;
|
||||
}
|
||||
|
||||
auto data_len = read_buf[start_pos++];
|
||||
|
@ -707,7 +705,7 @@ uint16_t KeyboardLayout::ExtractCodePage(const char *keyboard_file_name)
|
|||
if (submappings >= ceil_udivide(sizeof(read_buf) - start_pos - 0x14, 8u)) {
|
||||
LOG(LOG_BIOS, LOG_ERROR)
|
||||
("Keyboard layout file %s is corrupt", keyboard_file_name);
|
||||
return default_cp_437;
|
||||
return DefaultCodePage437;
|
||||
}
|
||||
|
||||
// check all submappings and use them if general submapping or same
|
||||
|
@ -720,111 +718,7 @@ uint16_t KeyboardLayout::ExtractCodePage(const char *keyboard_file_name)
|
|||
|
||||
if (submap_cp!=0) return submap_cp;
|
||||
}
|
||||
return default_cp_437;
|
||||
}
|
||||
|
||||
const char *get_builtin_cp_filename(const int codepage_id)
|
||||
{
|
||||
// reference:
|
||||
// https://gitlab.com/FreeDOS/base/cpidos/-/blob/master/DOC/CPIDOS/CODEPAGE.TXT
|
||||
switch (codepage_id) {
|
||||
case 437:
|
||||
case 850:
|
||||
case 852:
|
||||
case 853:
|
||||
case 857:
|
||||
case 858: return "EGA.CPX";
|
||||
case 775:
|
||||
case 859:
|
||||
case 1116:
|
||||
case 1117:
|
||||
case 1118:
|
||||
case 1119: return "EGA2.CPX";
|
||||
case 771:
|
||||
case 772:
|
||||
case 808:
|
||||
case 855:
|
||||
case 866:
|
||||
case 872: return "EGA3.CPX";
|
||||
case 848:
|
||||
case 849:
|
||||
case 1125:
|
||||
case 1131:
|
||||
case 3012:
|
||||
case 30010: return "EGA4.CPX";
|
||||
case 113:
|
||||
case 737:
|
||||
case 851:
|
||||
case 869: return "EGA5.CPX";
|
||||
case 899:
|
||||
case 30008:
|
||||
case 58210:
|
||||
case 59829:
|
||||
case 60258:
|
||||
case 60853: return "EGA6.CPX";
|
||||
case 30011:
|
||||
case 30013:
|
||||
case 30014:
|
||||
case 30017:
|
||||
case 30018:
|
||||
case 30019: return "EGA7.CPX";
|
||||
case 770:
|
||||
case 773:
|
||||
case 774:
|
||||
case 777:
|
||||
case 778: return "EGA8.CPX";
|
||||
case 860:
|
||||
case 861:
|
||||
case 863:
|
||||
case 865:
|
||||
case 867: return "EGA9.CPX";
|
||||
case 667:
|
||||
case 668:
|
||||
case 790:
|
||||
case 991:
|
||||
case 3845: return "EGA10.CPX";
|
||||
case 30000:
|
||||
case 30001:
|
||||
case 30004:
|
||||
case 30007:
|
||||
case 30009: return "EGA11.CPX";
|
||||
case 30003:
|
||||
case 30029:
|
||||
case 30030:
|
||||
case 58335: return "EGA12.CPX";
|
||||
case 895:
|
||||
case 30002:
|
||||
case 58152:
|
||||
case 59234:
|
||||
case 62306: return "EGA13.CPX";
|
||||
case 30006:
|
||||
case 30012:
|
||||
case 30015:
|
||||
case 30016:
|
||||
case 30020:
|
||||
case 30021: return "EGA14.CPX";
|
||||
case 30023:
|
||||
case 30024:
|
||||
case 30025:
|
||||
case 30026:
|
||||
case 30027:
|
||||
case 30028: return "EGA15.CPX";
|
||||
case 3021:
|
||||
case 30005:
|
||||
case 30022:
|
||||
case 30031:
|
||||
case 30032: return "EGA16.CPX";
|
||||
case 862:
|
||||
case 864:
|
||||
case 30034:
|
||||
case 30033:
|
||||
case 30039:
|
||||
case 30040: return "EGA17.CPX";
|
||||
case 856:
|
||||
case 3846:
|
||||
case 3848: return "EGA18.CPI";
|
||||
default: return ""; // none
|
||||
}
|
||||
return DefaultCodePage437;
|
||||
}
|
||||
|
||||
KeyboardErrorCode KeyboardLayout::ReadCodePageFile(const char *requested_cp_filename, const int32_t codepage_id)
|
||||
|
@ -836,7 +730,7 @@ KeyboardErrorCode KeyboardLayout::ReadCodePageFile(const char *requested_cp_file
|
|||
return KEYB_NOERROR;
|
||||
|
||||
if (cp_filename == "auto") {
|
||||
cp_filename = get_builtin_cp_filename(codepage_id);
|
||||
cp_filename = DOS_GetBundledCodePageFileName(codepage_id);
|
||||
if (cp_filename.empty()) {
|
||||
LOG_WARNING("CODEPAGE: Could not find a file for codepage ID %d", codepage_id);
|
||||
return KEYB_INVALIDCPFILE;
|
||||
|
@ -1066,7 +960,9 @@ KeyboardErrorCode KeyboardLayout::ReadCodePageFile(const char *requested_cp_file
|
|||
}
|
||||
INT10_SetupRomMemoryChecksum();
|
||||
|
||||
// convert UTF-8 [autoexec] section to new code page
|
||||
// re-create country information and [autoexec] section
|
||||
// to match new code page
|
||||
DOS_RefreshCountryInfo();
|
||||
AUTOEXEC_NotifyNewCodePage();
|
||||
|
||||
return KEYB_NOERROR;
|
||||
|
@ -1217,459 +1113,14 @@ KeyboardErrorCode DOS_SwitchKeyboardLayout(const char *new_layout, int32_t &trie
|
|||
}
|
||||
|
||||
// get currently loaded layout name (nullptr if no layout is loaded)
|
||||
const char* DOS_GetLoadedLayout(void) {
|
||||
const char* DOS_GetLoadedLayout()
|
||||
{
|
||||
if (loaded_layout) {
|
||||
return loaded_layout->GetLayoutName();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const std::map<std::string, Country> country_code_map{
|
||||
// clang-format off
|
||||
// reference: https://gitlab.com/FreeDOS/base/keyb_lay/-/blob/master/DOC/KEYB/LAYOUTS/LAYOUTS.TXT
|
||||
{"ar462", Country::Arabic },
|
||||
{"ar470", Country::Arabic },
|
||||
{"az", Country::Azerbaijan },
|
||||
{"ba", Country::Bosnia_Herzegovina },
|
||||
{"be", Country::Belgium },
|
||||
{"bg", Country::Bulgaria }, // 101-key
|
||||
{"bg103", Country::Bulgaria }, // 101-key, Phonetic
|
||||
{"bg241", Country::Bulgaria }, // 102-key
|
||||
{"bl", Country::Belarus },
|
||||
{"bn", Country::Benin },
|
||||
{"br", Country::Brazil }, // ABNT layout
|
||||
{"br274", Country::Brazil }, // US layout
|
||||
{"bx", Country::Belgium }, // International
|
||||
{"by", Country::Belarus },
|
||||
{"ca", Country::Canada_English }, // Standard
|
||||
{"ce", Country::Russia }, // Chechnya Standard
|
||||
{"ce443", Country::Russia }, // Chechnya Typewriter
|
||||
{"cg", Country::Montenegro },
|
||||
{"cf", Country::Canada_French }, // Standard
|
||||
{"cf445", Country::Canada_French }, // Dual-layer
|
||||
{"co", Country::United_States }, // Colemak
|
||||
{"cz", Country::Czechia }, // QWERTY
|
||||
{"cz243", Country::Czechia }, // Standard
|
||||
{"cz489", Country::Czechia }, // Programmers
|
||||
{"de", Country::Germany }, // Standard
|
||||
{"dk", Country::Denmark },
|
||||
{"dv", Country::United_States }, // Dvorak
|
||||
{"ee", Country::Estonia },
|
||||
{"el", Country::Greece }, // 319
|
||||
{"es", Country::Spain },
|
||||
{"et", Country::Estonia },
|
||||
{"fi", Country::Finland },
|
||||
{"fo", Country::Faroe_Islands },
|
||||
{"fr", Country::France }, // Standard
|
||||
{"fx", Country::France }, // International
|
||||
{"gk", Country::Greece }, // 319
|
||||
{"gk220", Country::Greece }, // 220
|
||||
{"gk459", Country::Greece }, // 101-key
|
||||
{"gr", Country::Germany }, // Standard
|
||||
{"gr453", Country::Germany }, // Dual-layer
|
||||
{"hr", Country::Croatia },
|
||||
{"hu", Country::Hungary }, // 101-key
|
||||
{"hu208", Country::Hungary }, // 102-key
|
||||
{"hy", Country::Armenia },
|
||||
{"il", Country::Israel },
|
||||
{"is", Country::Iceland }, // 101-key
|
||||
{"is161", Country::Iceland }, // 102-key
|
||||
{"it", Country::Italy }, // Standard
|
||||
{"it142", Country::Italy }, // Comma on Numeric Pad
|
||||
{"ix", Country::Italy }, // International
|
||||
{"jp", Country::Japan },
|
||||
{"ka", Country::Georgia },
|
||||
{"kk", Country::Kazakhstan },
|
||||
{"kk476", Country::Kazakhstan },
|
||||
{"kx", Country::United_Kingdom }, // International
|
||||
{"ky", Country::Kyrgyzstan },
|
||||
{"la", Country::Latin_America },
|
||||
{"lh", Country::United_States }, // Left-Hand Dvorak
|
||||
{"lt", Country::Lithuania }, // Baltic
|
||||
{"lt210", Country::Lithuania }, // 101-key, Programmers
|
||||
{"lt211", Country::Lithuania }, // AZERTY
|
||||
{"lt221", Country::Lithuania }, // Standard
|
||||
{"lt456", Country::Lithuania }, // Dual-layout
|
||||
{"lv", Country::Latvia }, // Standard
|
||||
{"lv455", Country::Latvia }, // Dual-layout
|
||||
{"ml", Country::Malta }, // UK-based
|
||||
{"mk", Country::Macedonia },
|
||||
{"mn", Country::Mongolia },
|
||||
{"mo", Country::Mongolia },
|
||||
{"mt", Country::Malta }, // UK-based
|
||||
{"mt103", Country::Malta }, // US-based
|
||||
{"ne", Country::Niger },
|
||||
{"ng", Country::Nigeria },
|
||||
{"nl", Country::Netherlands }, // 102-key
|
||||
{"no", Country::Norway },
|
||||
{"ph", Country::Philippines },
|
||||
{"pl", Country::Poland }, // 101-key, Programmers
|
||||
{"pl214", Country::Poland }, // 102-key
|
||||
{"po", Country::Portugal },
|
||||
{"px", Country::Portugal }, // International
|
||||
{"ro", Country::Romania }, // Standard
|
||||
{"ro446", Country::Romania }, // QWERTY
|
||||
{"rh", Country::United_States }, // Right-Hand Dvorak
|
||||
{"ru", Country::Russia }, // Standard
|
||||
{"ru443", Country::Russia }, // Typewriter
|
||||
{"rx", Country::Russia }, // Extended Standard
|
||||
{"rx443", Country::Russia }, // Extended Typewriter
|
||||
{"sd", Country::Switzerland }, // German
|
||||
{"sf", Country::Switzerland }, // French
|
||||
{"sg", Country::Switzerland }, // German
|
||||
{"si", Country::Slovenia },
|
||||
{"sk", Country::Slovakia },
|
||||
{"sp", Country::Spain },
|
||||
{"sq", Country::Albania }, // No-deadkeys
|
||||
{"sq448", Country::Albania }, // Deadkeys
|
||||
{"sr", Country::Serbia }, // Deadkey
|
||||
{"su", Country::Finland },
|
||||
{"sv", Country::Sweden },
|
||||
{"sx", Country::Spain }, // International
|
||||
{"tj", Country::Tadjikistan },
|
||||
{"tm", Country::Turkmenistan },
|
||||
{"tr", Country::Turkey }, // QWERTY
|
||||
{"tr440", Country::Turkey }, // Non-standard
|
||||
{"tt", Country::Russia }, // Tatarstan Standard
|
||||
{"tt443", Country::Russia }, // Tatarstan Typewriter
|
||||
{"ua", Country::Ukraine }, // 101-key
|
||||
{"uk", Country::United_Kingdom }, // Standard
|
||||
{"uk168", Country::United_Kingdom }, // Allternate
|
||||
{"ur", Country::Ukraine }, // 101-key
|
||||
{"ur465", Country::Ukraine }, // 101-key
|
||||
{"ur1996", Country::Ukraine }, // 101-key
|
||||
{"ur2001", Country::Ukraine }, // 102-key
|
||||
{"ur2007", Country::Ukraine }, // 102-key
|
||||
{"us", Country::United_States }, // Standard
|
||||
{"ux", Country::United_States }, // International
|
||||
{"uz", Country::Uzbekistan },
|
||||
{"vi", Country::Vietnam },
|
||||
{"yc", Country::Serbia }, // Deadkey
|
||||
{"yc450", Country::Serbia }, // No-deadkey
|
||||
{"yu", Country::Yugoslavia },
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
static const std::map<std::string, const char *> language_to_layout_exception_map{
|
||||
{"nl", "us"},
|
||||
};
|
||||
|
||||
static bool lookup_country_from_code(const char *country_code, Country &country)
|
||||
{
|
||||
if (country_code) {
|
||||
const auto it = country_code_map.find(country_code);
|
||||
if (it != country_code_map.end()) {
|
||||
country = it->second;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const char *lookup_language_to_layout_exception(const char *language_code)
|
||||
{
|
||||
if (language_code) {
|
||||
const auto it = language_to_layout_exception_map.find(language_code);
|
||||
if (it != language_to_layout_exception_map.end())
|
||||
return it->second;
|
||||
}
|
||||
return language_code;
|
||||
}
|
||||
|
||||
uint16_t assert_codepage(const uint16_t codepage)
|
||||
{
|
||||
assert(!sv{get_builtin_cp_filename(codepage)}.empty());
|
||||
return codepage;
|
||||
}
|
||||
|
||||
[[maybe_unused]] constexpr uint16_t lookup_codepage_from_country(const Country country)
|
||||
{
|
||||
// grouped in ascending ordered by codepage value
|
||||
switch (country) {
|
||||
|
||||
case Country::Australia:
|
||||
case Country::China:
|
||||
case Country::Hong_Kong:
|
||||
case Country::India:
|
||||
case Country::Ireland:
|
||||
case Country::Japan:
|
||||
case Country::Korea:
|
||||
case Country::Malaysia:
|
||||
case Country::New_Zealand:
|
||||
case Country::Singapore:
|
||||
case Country::South_Africa:
|
||||
case Country::Taiwan:
|
||||
case Country::United_Kingdom:
|
||||
case Country::United_States: return assert_codepage(437);
|
||||
|
||||
case Country::Poland: return assert_codepage(668);
|
||||
|
||||
case Country::Lithuania: return assert_codepage(774);
|
||||
|
||||
case Country::Argentina:
|
||||
case Country::Austria:
|
||||
case Country::Belgium:
|
||||
case Country::Canada_English:
|
||||
case Country::Chile:
|
||||
case Country::Colombia:
|
||||
case Country::Ecuador:
|
||||
case Country::Finland:
|
||||
case Country::France:
|
||||
case Country::Germany:
|
||||
case Country::Italy:
|
||||
case Country::Latin_America:
|
||||
case Country::Mexico:
|
||||
case Country::Netherlands:
|
||||
case Country::Philippines:
|
||||
case Country::Spain:
|
||||
case Country::Sweden:
|
||||
case Country::Switzerland:
|
||||
case Country::Venezuela: return assert_codepage(850);
|
||||
|
||||
case Country::Albania:
|
||||
case Country::Croatia:
|
||||
case Country::Montenegro:
|
||||
case Country::Romania:
|
||||
case Country::Slovenia:
|
||||
case Country::Turkmenistan: return assert_codepage(852);
|
||||
|
||||
case Country::Malta: return assert_codepage(853);
|
||||
|
||||
case Country::Bosnia_Herzegovina:
|
||||
case Country::Bulgaria:
|
||||
case Country::Macedonia:
|
||||
case Country::Serbia:
|
||||
case Country::Yugoslavia: return assert_codepage(855);
|
||||
|
||||
case Country::Turkey: return assert_codepage(857);
|
||||
|
||||
case Country::Brazil:
|
||||
case Country::Portugal: return assert_codepage(860);
|
||||
|
||||
case Country::Faroe_Islands:
|
||||
case Country::Iceland: return assert_codepage(861);
|
||||
|
||||
case Country::Israel: return assert_codepage(862);
|
||||
|
||||
case Country::Canada_French: return assert_codepage(863);
|
||||
|
||||
case Country::Arabic: return assert_codepage(864);
|
||||
|
||||
case Country::Denmark:
|
||||
case Country::Norway: return assert_codepage(865);
|
||||
|
||||
case Country::Russia: return assert_codepage(866);
|
||||
|
||||
case Country::Czechia:
|
||||
case Country::Slovakia: return assert_codepage(867);
|
||||
|
||||
case Country::Greece: return assert_codepage(869);
|
||||
|
||||
case Country::Armenia: return assert_codepage(899);
|
||||
|
||||
case Country::Estonia: return assert_codepage(1116);
|
||||
|
||||
case Country::Latvia: return assert_codepage(1117);
|
||||
|
||||
case Country::Ukraine: return assert_codepage(1125);
|
||||
|
||||
case Country::Belarus: return assert_codepage(1131);
|
||||
|
||||
case Country::Hungary: return assert_codepage(3845);
|
||||
|
||||
case Country::Tadjikistan: return assert_codepage(30002);
|
||||
|
||||
case Country::Nigeria: return assert_codepage(30005);
|
||||
|
||||
case Country::Vietnam: return assert_codepage(30006);
|
||||
|
||||
case Country::Benin: return assert_codepage(30027);
|
||||
|
||||
case Country::Niger: return assert_codepage(30028);
|
||||
|
||||
case Country::Kazakhstan:
|
||||
case Country::Kyrgyzstan:
|
||||
case Country::Mongolia: return assert_codepage(58152);
|
||||
|
||||
case Country::Azerbaijan: return assert_codepage(58210);
|
||||
|
||||
case Country::Georgia: return assert_codepage(59829);
|
||||
|
||||
case Country::Uzbekistan: return assert_codepage(62306);
|
||||
|
||||
default:
|
||||
LOG_WARNING("LAYOUT: No default code page for country %d", static_cast<int>(country));
|
||||
return assert_codepage(default_cp_437);
|
||||
}
|
||||
}
|
||||
|
||||
// Use OS-specific calls to extra the layout and from there convert it into a language
|
||||
std::string get_lang_from_host_layout()
|
||||
{
|
||||
#if defined(WIN32)
|
||||
# include <windows.h>
|
||||
|
||||
WORD cur_kb_layout = LOWORD(GetKeyboardLayout(0));
|
||||
WORD cur_kb_sub_id = 0;
|
||||
char layout_id_string[KL_NAMELENGTH];
|
||||
|
||||
auto parse_hex_string = [](const char *s) {
|
||||
uint32_t value = 0;
|
||||
sscanf(s, "%x", &value);
|
||||
return value;
|
||||
};
|
||||
|
||||
if (GetKeyboardLayoutName(layout_id_string)) {
|
||||
if (safe_strlen(layout_id_string) == 8) {
|
||||
const int cur_kb_layout_by_name = parse_hex_string(
|
||||
(char *)&layout_id_string[4]);
|
||||
layout_id_string[4] = 0;
|
||||
const int sub_id = parse_hex_string(
|
||||
(char *)&layout_id_string[0]);
|
||||
if ((cur_kb_layout_by_name > 0) &&
|
||||
(cur_kb_layout_by_name < 65536)) {
|
||||
// use layout _id extracted from the layout string
|
||||
cur_kb_layout = (WORD)cur_kb_layout_by_name;
|
||||
}
|
||||
if ((sub_id >= 0) && (sub_id < 100)) {
|
||||
// use sublanguage ID extracted from the layout
|
||||
// string
|
||||
cur_kb_sub_id = (WORD)sub_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
// try to match emulated keyboard layout with host-keyboardlayout
|
||||
switch (cur_kb_layout) {
|
||||
case 1025: // Saudi Arabia
|
||||
case 1119: // Tamazight
|
||||
case 1120: // Kashmiri
|
||||
case 2049: // Iraq
|
||||
case 3073: // Egypt
|
||||
case 4097: // Libya
|
||||
case 5121: // Algeria
|
||||
case 6145: // Morocco
|
||||
case 7169: // Tunisia
|
||||
case 8193: // Oman
|
||||
case 9217: // Yemen
|
||||
case 10241: // Syria
|
||||
case 11265: // Jordan
|
||||
case 12289: // Lebanon
|
||||
case 13313: // Kuwait
|
||||
case 14337: // U.A.E
|
||||
case 15361: // Bahrain
|
||||
case 16385: // Qatar
|
||||
return "ar462";
|
||||
|
||||
case 1026: return "bg"; // Bulgarian
|
||||
case 1029: return "cz243"; // Czech
|
||||
case 1030: return "dk"; // Danish
|
||||
|
||||
case 2055: // German - Switzerland
|
||||
case 3079: // German - Austria
|
||||
case 4103: // German - Luxembourg
|
||||
case 5127: // German - Liechtenstein
|
||||
case 1031: // German - Germany
|
||||
return "gr";
|
||||
|
||||
case 1032: return "gk"; // Greek
|
||||
case 1034: return "sp"; // Spanish - Spain (Traditional Sort)
|
||||
case 1035: return "su"; // Finnish
|
||||
|
||||
case 1036: // French - France
|
||||
case 2060: // French - Belgium
|
||||
case 4108: // French - Switzerland
|
||||
case 5132: // French - Luxembourg
|
||||
case 6156: // French - Monaco
|
||||
case 7180: // French - West Indies
|
||||
case 8204: // French - Reunion
|
||||
case 9228: // French - Democratic Rep. of Congo
|
||||
case 10252: // French - Senegal
|
||||
case 11276: // French - Cameroon
|
||||
case 12300: // French - Cote d'Ivoire
|
||||
case 13324: // French - Mali
|
||||
case 14348: // French - Morocco
|
||||
case 15372: // French - Haiti
|
||||
case 58380: // French - North Africa
|
||||
return "fr";
|
||||
|
||||
case 1037: return "il"; // Hebrew
|
||||
case 1038: return cur_kb_sub_id ? "hu" : "hu208";
|
||||
case 1039: return "is161"; // Icelandic
|
||||
|
||||
case 2064: // Italian - Switzerland
|
||||
case 1040: // Italian - Italy
|
||||
return "it";
|
||||
|
||||
case 3084: return "ca"; // French - Canada
|
||||
case 1041: return "jp"; // Japanese
|
||||
|
||||
case 2067: // Dutch - Belgium
|
||||
case 1043: // Dutch - Netherlands
|
||||
return "nl";
|
||||
|
||||
case 1044: return "no"; // Norwegian (Bokmål)
|
||||
case 1045: return "pl"; // Polish
|
||||
case 1046: return "br"; // Portuguese - Brazil
|
||||
|
||||
case 2073: // Russian - Moldava
|
||||
case 1049: // Russian
|
||||
return "ru";
|
||||
|
||||
case 4122: // Croatian (Bosnia/Herzegovina)
|
||||
case 1050: // Croatian
|
||||
return "hr";
|
||||
|
||||
case 1051: return "sk"; // Slovak
|
||||
case 1052: return "sq"; // Albanian - Albania
|
||||
|
||||
case 2077: // Swedish - Finland
|
||||
case 1053: // Swedish
|
||||
return "sv";
|
||||
|
||||
case 1055: return "tr"; // Turkish
|
||||
case 1058: return "ur"; // Ukrainian
|
||||
case 1059: return "bl"; // Belarusian
|
||||
case 1060: return "si"; // Slovenian
|
||||
case 1061: return "et"; // Estonian
|
||||
case 1062: return "lv"; // Latvian
|
||||
case 1063: return "lt"; // Lithuanian
|
||||
case 1064: return "tj"; // Tajik
|
||||
case 1066: return "vi"; // Vietnamese
|
||||
case 1067: return "hy"; // Armenian - Armenia
|
||||
case 1071: return "mk"; // F.Y.R.O. Macedonian
|
||||
case 1079: return "ka"; // Georgian
|
||||
case 2070: return "po"; // Portuguese - Portugal
|
||||
case 2072: return "ro"; // Romanian - Moldava
|
||||
case 5146: return "ba"; // Bosnian (Bosnia/Herzegovina)
|
||||
|
||||
case 2058: // Spanish - Mexico
|
||||
case 3082: // Spanish - Spain (Modern Sort)
|
||||
case 4106: // Spanish - Guatemala
|
||||
case 5130: // Spanish - Costa Rica
|
||||
case 6154: // Spanish - Panama
|
||||
case 7178: // Spanish - Dominican Republic
|
||||
case 8202: // Spanish - Venezuela
|
||||
case 9226: // Spanish - Colombia
|
||||
case 10250: // Spanish - Peru
|
||||
case 11274: // Spanish - Argentina
|
||||
case 12298: // Spanish - Ecuador
|
||||
case 13322: // Spanish - Chile
|
||||
case 14346: // Spanish - Uruguay
|
||||
case 15370: // Spanish - Paraguay
|
||||
case 16394: // Spanish - Bolivia
|
||||
case 17418: // Spanish - El Salvador
|
||||
case 18442: // Spanish - Honduras
|
||||
case 19466: // Spanish - Nicaragua
|
||||
case 20490: // Spanish - Puerto Rico
|
||||
case 21514: // Spanish - United States
|
||||
case 58378: // Spanish - Latin America
|
||||
return "la";
|
||||
}
|
||||
|
||||
#endif
|
||||
return ""; // default to empty/US
|
||||
}
|
||||
|
||||
// A helper that loads a layout given only a language
|
||||
KeyboardErrorCode DOS_LoadKeyboardLayoutFromLanguage(const char * language_pref)
|
||||
{
|
||||
|
@ -1677,52 +1128,69 @@ KeyboardErrorCode DOS_LoadKeyboardLayoutFromLanguage(const char * language_pref)
|
|||
|
||||
// If a specific language wasn't provided, get it from setup
|
||||
std::string language = language_pref;
|
||||
if (language == "auto")
|
||||
if (language == "auto") {
|
||||
language = control->GetLanguage();
|
||||
}
|
||||
|
||||
// TODO: This code mixes language code with keyboard layout; this should
|
||||
// be cleaned up eventually, possibly we should use
|
||||
// 'use get_language_from_os()' from 'cross.h'
|
||||
|
||||
// Does the language have a country associate with it?
|
||||
auto country = default_country;
|
||||
bool found_country = lookup_country_from_code(language.c_str(), country);
|
||||
auto country = DOS_GetDefaultCountry();
|
||||
bool found_country = DOS_GetCountryFromLayout(language, country);
|
||||
|
||||
// If we can't find a country for the language, try from the host
|
||||
if (!found_country) {
|
||||
language = get_lang_from_host_layout();
|
||||
found_country = lookup_country_from_code(language.c_str(), country);
|
||||
language = DOS_GetLayoutFromHost();
|
||||
found_country = DOS_GetCountryFromLayout(language, country);
|
||||
}
|
||||
// Inform the user if we couldn't find a valid country
|
||||
if (!language.empty() && !found_country) {
|
||||
LOG_WARNING("LAYOUT: A country could not be found for the language: %s",
|
||||
LOG_WARNING("DOS: A country could not be found for the language: %s",
|
||||
language.c_str());
|
||||
}
|
||||
|
||||
// Regardless of the above, carry on with setting up the layout
|
||||
const auto codepage = lookup_codepage_from_country(country);
|
||||
const auto layout = lookup_language_to_layout_exception(language.c_str());
|
||||
const auto result = DOS_LoadKeyboardLayout(layout, codepage, "auto");
|
||||
const auto codepage = DOS_GetCodePageFromCountry(country);
|
||||
const auto layout = DOS_CheckLanguageToLayoutException(language);
|
||||
const auto result = DOS_LoadKeyboardLayout(layout.c_str(), codepage, "auto");
|
||||
|
||||
if (result == KEYB_NOERROR) {
|
||||
LOG_MSG("LAYOUT: Loaded codepage %d for detected language %s", codepage, language.c_str());
|
||||
} else if (country != default_country) {
|
||||
LOG_WARNING("LAYOUT: Failed loading codepage %d for detected language %s", codepage, language.c_str());
|
||||
LOG_MSG("DOS: Loaded codepage %d for detected language '%s'",
|
||||
codepage,
|
||||
language.c_str());
|
||||
} else if (country != DOS_GetDefaultCountry()) {
|
||||
LOG_WARNING("DOS: Failed loading codepage %d for detected language '%s'",
|
||||
codepage,
|
||||
language.c_str());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
class DOS_KeyboardLayout final : public Module_base {
|
||||
public:
|
||||
DOS_KeyboardLayout(Section* configuration):Module_base(configuration){
|
||||
Section_prop * section=static_cast<Section_prop *>(configuration);
|
||||
DOS_KeyboardLayout(Section* configuration) : Module_base(configuration)
|
||||
{
|
||||
Section_prop* section = static_cast<Section_prop*>(configuration);
|
||||
assert(section);
|
||||
|
||||
dos.loaded_codepage = default_cp_437; // US codepage already initialized
|
||||
// US codepage already initialized
|
||||
dos.loaded_codepage = DefaultCodePage437;
|
||||
|
||||
loaded_layout = std::make_unique<KeyboardLayout>();
|
||||
|
||||
std::string layoutname = section->Get_string("keyboardlayout");
|
||||
|
||||
// If the use only provided a single value (language), then try using it
|
||||
constexpr bool reason_keyboard_layout = true;
|
||||
const auto layout_is_one_value = sv(layoutname).find(' ') == std::string::npos;
|
||||
if (layout_is_one_value) {
|
||||
if (!DOS_LoadKeyboardLayoutFromLanguage(layoutname.c_str())) {
|
||||
return; // success
|
||||
// Success - re-create country information to
|
||||
// match new keyboard layout
|
||||
DOS_RefreshCountryInfo(reason_keyboard_layout);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Otherwise use the layout to get the codepage
|
||||
|
@ -1731,24 +1199,30 @@ public:
|
|||
|
||||
if (loaded_layout->ReadKeyboardFile(layoutname.c_str(), dos.loaded_codepage)) {
|
||||
if (strncmp(layoutname.c_str(), "auto", 4)) {
|
||||
LOG_ERR("LAYOUT: Failed to load keyboard layout %s",
|
||||
LOG_ERR("DOS: Failed to load keyboard layout '%s'",
|
||||
layoutname.c_str());
|
||||
}
|
||||
} else {
|
||||
const char *lcode = loaded_layout->GetMainLanguageCode();
|
||||
if (lcode) {
|
||||
LOG_MSG("LAYOUT: DOS keyboard layout loaded with main language code %s for layout %s",lcode,layoutname.c_str());
|
||||
LOG_MSG("DOS: Loaded keyboard layout '%s' with main language code '%s'",
|
||||
layoutname.c_str(),
|
||||
lcode);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert UTF-8 [autoexec] section to new code page
|
||||
// Re-create country information and [autoexec] section
|
||||
// to match new code page and keyboard layout
|
||||
DOS_RefreshCountryInfo(reason_keyboard_layout);
|
||||
AUTOEXEC_NotifyNewCodePage();
|
||||
}
|
||||
|
||||
~DOS_KeyboardLayout(){
|
||||
if ((dos.loaded_codepage != default_cp_437) && (CurMode->type == M_TEXT)) {
|
||||
~DOS_KeyboardLayout()
|
||||
{
|
||||
if ((dos.loaded_codepage != DefaultCodePage437) &&
|
||||
(CurMode->type == M_TEXT)) {
|
||||
INT10_ReloadRomFonts();
|
||||
dos.loaded_codepage = default_cp_437; // US codepage
|
||||
dos.loaded_codepage = DefaultCodePage437; // US codepage
|
||||
}
|
||||
if (loaded_layout) {
|
||||
loaded_layout.reset();
|
||||
|
@ -1762,28 +1236,6 @@ void DOS_KeyboardLayout_ShutDown(Section* /*sec*/) {
|
|||
KeyboardLayout.reset();
|
||||
}
|
||||
|
||||
void DOS_SetCountry(uint16_t countryNo);
|
||||
|
||||
static void set_country_from_pref(const int country_pref)
|
||||
{
|
||||
// default to the US
|
||||
auto country_number = static_cast<uint16_t>(default_country);
|
||||
|
||||
// If the country preference is valid, use it
|
||||
if (country_pref > 0) {
|
||||
country_number = static_cast<uint16_t>(country_pref);
|
||||
} else if (const auto country_code = DOS_GetLoadedLayout(); country_code) {
|
||||
if (Country c; lookup_country_from_code(country_code, c)) {
|
||||
country_number = static_cast<uint16_t>(c);
|
||||
} else {
|
||||
LOG_ERR("LANGUAGE: The layout's country code: '%s' does not have a corresponding country",
|
||||
country_code);
|
||||
}
|
||||
}
|
||||
|
||||
DOS_SetCountry(country_number);
|
||||
}
|
||||
|
||||
void DOS_KeyboardLayout_Init(Section *sec)
|
||||
{
|
||||
assert(sec);
|
||||
|
@ -1791,7 +1243,4 @@ void DOS_KeyboardLayout_Init(Section *sec)
|
|||
|
||||
constexpr auto changeable_at_runtime = true;
|
||||
sec->AddDestroyFunction(&DOS_KeyboardLayout_ShutDown, changeable_at_runtime);
|
||||
|
||||
const auto settings = static_cast<const Section_prop *>(sec);
|
||||
set_country_from_pref(settings->Get_int("country"));
|
||||
}
|
||||
|
|
2908
src/dos/dos_locale.cpp
Normal file
2908
src/dos/dos_locale.cpp
Normal file
File diff suppressed because it is too large
Load diff
59
src/dos/dos_locale.h
Normal file
59
src/dos/dos_locale.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Copyright (C) 2020-2023 The DOSBox Staging Team
|
||||
* Copyright (C) 2002-2021 The DOSBox Team
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef DOSBOX_DOS_LOCALE_H
|
||||
#define DOSBOX_DOS_LOCALE_H
|
||||
|
||||
#include "setup.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
constexpr uint16_t DefaultCodePage437 = 437;
|
||||
|
||||
std::string DOS_GenerateListCountriesMessage();
|
||||
|
||||
bool DOS_SetCountry(const uint16_t country_id);
|
||||
uint16_t DOS_GetCountry();
|
||||
void DOS_RefreshCountryInfo(const bool reason_keyboard_layout = false);
|
||||
|
||||
std::string DOS_GetBundledCodePageFileName(const uint16_t code_page);
|
||||
|
||||
uint16_t DOS_GetCodePageFromCountry(const uint16_t country);
|
||||
|
||||
std::string DOS_CheckLanguageToLayoutException(const std::string& language_code);
|
||||
|
||||
uint16_t DOS_GetDefaultCountry();
|
||||
|
||||
bool DOS_GetCountryFromLayout(const std::string& layout, uint16_t& country);
|
||||
|
||||
std::string DOS_GetLayoutFromHost();
|
||||
|
||||
// Lifecycle
|
||||
|
||||
void DOS_Locale_Init(Section* sec);
|
||||
|
||||
// We need a separate function to support '--list-countries' command line switch
|
||||
// (and possibly others in the future) - it needs translated strings, but does
|
||||
// not initialize DOSBox fully.
|
||||
void DOS_Locale_AddMessages();
|
||||
|
||||
#endif
|
|
@ -9,6 +9,7 @@ libdos_sources = files(
|
|||
'dos_files.cpp',
|
||||
'dos_ioctl.cpp',
|
||||
'dos_keyboard_layout.cpp',
|
||||
'dos_locale.cpp',
|
||||
'dos_memory.cpp',
|
||||
'dos_misc.cpp',
|
||||
'dos_mscdex.cpp',
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "cpu.h"
|
||||
#include "cross.h"
|
||||
#include "debug.h"
|
||||
#include "dos/dos_locale.h"
|
||||
#include "dos_inc.h"
|
||||
#include "hardware.h"
|
||||
#include "inout.h"
|
||||
|
@ -1154,11 +1155,22 @@ void DOSBOX_Init()
|
|||
|
||||
// DOS locale settings
|
||||
|
||||
pint = secprop->Add_int("country", when_idle, 0);
|
||||
pint->Set_help("Set DOS country code (0 by default).\n"
|
||||
"This affects country-specific information such as date, time, and decimal\n"
|
||||
"formats. If set to 0, the country code corresponding to the selected keyboard\n"
|
||||
"layout will be used.");
|
||||
secprop->AddInitFunction(&DOS_Locale_Init, changeable_at_runtime);
|
||||
pstring = secprop->Add_string("locale_period", when_idle, "modern");
|
||||
pstring->Set_help(
|
||||
"Set locale epoch ('modern' by default). Historic settings (if available\n"
|
||||
"for the given country) try to mimic old DOS behaviour when displaying\n"
|
||||
"information such as dates, time, or numbers, modern ones follow current day\n"
|
||||
"practices for user experience more consistent with typical host systems.");
|
||||
pstring->Set_values({"historic", "modern"});
|
||||
|
||||
pstring = secprop->Add_string("country", when_idle, "auto");
|
||||
pstring->Set_help(
|
||||
"Set DOS country code ('auto' by default).\n"
|
||||
"This affects country-specific information such as date, time, and decimal\n"
|
||||
"formats. The list of supported country codes can be displayed using\n"
|
||||
"'--list-countries' command-line argument. If set to 'auto', the country code\n"
|
||||
"corresponding to the selected keyboard layout will be used.");
|
||||
|
||||
secprop->AddInitFunction(&DOS_KeyboardLayout_Init, changeable_at_runtime);
|
||||
pstring = secprop->Add_string("keyboardlayout", when_idle, "auto");
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#endif
|
||||
|
||||
#include "../capture/capture.h"
|
||||
#include "../dos/dos_locale.h"
|
||||
#include "../ints/int10.h"
|
||||
#include "control.h"
|
||||
#include "cpu.h"
|
||||
|
@ -4115,7 +4116,7 @@ static std::vector<std::string> get_sdl_texture_renderers()
|
|||
return drivers;
|
||||
}
|
||||
|
||||
static void messages_add_sdl()
|
||||
static void messages_add_command_line()
|
||||
{
|
||||
MSG_Add("DOSBOX_HELP",
|
||||
"Usage: %s [OPTION]... [PATH]\n"
|
||||
|
@ -4151,8 +4152,11 @@ static void messages_add_sdl()
|
|||
" --working-dir <path> Set working directory to <path>. DOSBox will act as if\n"
|
||||
" started from this directory.\n"
|
||||
"\n"
|
||||
" --list-countries List all supported countries with their numeric codes.\n"
|
||||
" Codes are to be used in the 'country' config setting.\n"
|
||||
"\n"
|
||||
" --list-glshaders List all available OpenGL shaders and their paths.\n"
|
||||
" Results are useable in the 'glshader = ' config setting.\n"
|
||||
" Shaders are to be used in the 'glshader' config setting.\n"
|
||||
"\n"
|
||||
" --fullscreen Start in fullscreen mode.\n"
|
||||
"\n"
|
||||
|
@ -4183,7 +4187,10 @@ static void messages_add_sdl()
|
|||
" -h, -?, --help Print help message and exit.\n"
|
||||
"\n"
|
||||
" -V, --version Print version information and exit.\n");
|
||||
}
|
||||
|
||||
static void messages_add_sdl()
|
||||
{
|
||||
MSG_Add("PROGRAM_CONFIG_PROPERTY_ERROR", "No such section or property: %s\n");
|
||||
|
||||
MSG_Add("PROGRAM_CONFIG_NO_PROPERTY",
|
||||
|
@ -4484,22 +4491,25 @@ void Restart(bool pressed) { // mapper handler
|
|||
restart_program(control->startup_params);
|
||||
}
|
||||
|
||||
static int list_glshaders()
|
||||
static void list_glshaders()
|
||||
{
|
||||
#if C_OPENGL
|
||||
for (const auto& line : RENDER_GenerateShaderInventoryMessage()) {
|
||||
printf("%s\n", line.c_str());
|
||||
printf_utf8("%s\n", line.c_str());
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
fprintf(stderr,
|
||||
"OpenGL is not supported by this executable "
|
||||
"and is missing the functionality to list shaders.");
|
||||
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void list_countries()
|
||||
{
|
||||
const auto message_utf8 = DOS_GenerateListCountriesMessage();
|
||||
printf_utf8("%s\n", message_utf8.c_str());
|
||||
}
|
||||
|
||||
static int print_primary_config_location()
|
||||
{
|
||||
const auto path = GetPrimaryConfigPath();
|
||||
|
@ -4591,7 +4601,7 @@ int sdl_main(int argc, char* argv[])
|
|||
loguru::g_preamble_pipe = true;
|
||||
|
||||
if (arguments->version || arguments->help || arguments->printconf ||
|
||||
arguments->editconf || arguments->eraseconf ||
|
||||
arguments->editconf || arguments->eraseconf || arguments->list_countries ||
|
||||
arguments->list_glshaders || arguments->erasemapper) {
|
||||
loguru::g_stderr_verbosity = loguru::Verbosity_WARNING;
|
||||
}
|
||||
|
@ -4630,7 +4640,10 @@ int sdl_main(int argc, char* argv[])
|
|||
//
|
||||
InitConfigDir();
|
||||
|
||||
// Register sdlmain's messages and conf sections
|
||||
// Register sdlmain's messages, conf sections, and essential
|
||||
// DOS messages, needed by some command line switches
|
||||
messages_add_command_line();
|
||||
DOS_Locale_AddMessages();
|
||||
messages_add_sdl();
|
||||
config_add_sdl();
|
||||
|
||||
|
@ -4679,18 +4692,7 @@ int sdl_main(int argc, char* argv[])
|
|||
const auto program_name = argv[0];
|
||||
const auto help_utf8 = format_string(MSG_GetRaw("DOSBOX_HELP"),
|
||||
program_name);
|
||||
#ifdef WIN32
|
||||
const auto old_code_page = GetConsoleOutputCP();
|
||||
|
||||
constexpr uint16_t CodePageUtf8 = 65001;
|
||||
SetConsoleOutputCP(CodePageUtf8);
|
||||
printf("%s", help_utf8.c_str());
|
||||
SetConsoleOutputCP(old_code_page);
|
||||
#else
|
||||
// Assume any OS without special support uses UTF-8 as
|
||||
// console encoding
|
||||
printf("%s", help_utf8.c_str());
|
||||
#endif
|
||||
printf_utf8("%s", help_utf8.c_str());
|
||||
return 0;
|
||||
}
|
||||
if (arguments->editconf) {
|
||||
|
@ -4705,8 +4707,13 @@ int sdl_main(int argc, char* argv[])
|
|||
if (arguments->printconf) {
|
||||
return print_primary_config_location();
|
||||
}
|
||||
if (arguments->list_countries) {
|
||||
list_countries();
|
||||
return 0;
|
||||
}
|
||||
if (arguments->list_glshaders) {
|
||||
return list_glshaders();
|
||||
list_glshaders();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Can't disable the console with debugger enabled
|
||||
|
|
|
@ -1773,6 +1773,7 @@ void Config::ParseArguments()
|
|||
arguments.noprimaryconf = cmdline->FindRemoveBoolArgument("noprimaryconf");
|
||||
arguments.nolocalconf = cmdline->FindRemoveBoolArgument("nolocalconf");
|
||||
arguments.fullscreen = cmdline->FindRemoveBoolArgument("fullscreen");
|
||||
arguments.list_countries = cmdline->FindRemoveBoolArgument("list-countries");
|
||||
arguments.list_glshaders = cmdline->FindRemoveBoolArgument("list-glshaders");
|
||||
arguments.noconsole = cmdline->FindRemoveBoolArgument("noconsole");
|
||||
arguments.startmapper = cmdline->FindRemoveBoolArgument("startmapper");
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "callback.h"
|
||||
#include "control.h"
|
||||
#include "cross.h"
|
||||
#include "dos_inc.h"
|
||||
#include "drives.h"
|
||||
#include "paging.h"
|
||||
#include "regs.h"
|
||||
|
@ -557,7 +558,7 @@ std::string format_number(const size_t num)
|
|||
std::string buffer = {};
|
||||
|
||||
std::string separator = " ";
|
||||
separator[0] = dos.tables.country[DOS_THOUSANDS_SEPARATOR_OFS];
|
||||
separator[0] = DOS_GetLocaleThousandsSeparator();
|
||||
|
||||
auto tmp = num;
|
||||
while (tmp) {
|
||||
|
@ -694,19 +695,19 @@ char *format_date(const uint16_t year, const uint8_t month, const uint8_t day)
|
|||
{
|
||||
char format_string[6];
|
||||
static char return_date_buffer[15] = {0};
|
||||
const char date_format = dos.tables.country[DOS_DATE_FORMAT_OFS];
|
||||
const char date_separator = dos.tables.country[DOS_DATE_SEPARATOR_OFS];
|
||||
const auto date_format = DOS_GetLocaleDateFormat();
|
||||
const char date_separator = DOS_GetLocaleDateSeparator();
|
||||
int result;
|
||||
switch (date_format) {
|
||||
case 1:
|
||||
case DosDateFormat::DayMonthYear:
|
||||
result = sprintf(format_string, "D%cM%cY", date_separator,
|
||||
date_separator);
|
||||
break;
|
||||
case 2:
|
||||
case DosDateFormat::YearMonthDay:
|
||||
result = sprintf(format_string, "Y%cM%cD", date_separator,
|
||||
date_separator);
|
||||
break;
|
||||
default:
|
||||
default: // DosDateFormat::MonthDayYear
|
||||
result = sprintf(format_string, "M%cD%cY", date_separator,
|
||||
date_separator);
|
||||
}
|
||||
|
@ -747,16 +748,15 @@ char* format_time(const uint8_t hour, const uint8_t min, const uint8_t sec,
|
|||
uint8_t fhour = hour;
|
||||
static char return_time_buffer[19] = {0};
|
||||
char ampm[3] = "";
|
||||
char time_format = dos.tables.country[DOS_TIME_FORMAT_OFS];
|
||||
if (!time_format) { // 12 hour notation?
|
||||
if (DOS_GetLocaleTimeFormat() == DosTimeFormat::Time12H) {
|
||||
if (fhour != 12)
|
||||
fhour %= 12;
|
||||
strcpy(ampm, hour != 12 && hour == fhour ? "am" : "pm");
|
||||
if (!full)
|
||||
*(ampm + 1) = 0; // "a" or "p" in short time format
|
||||
}
|
||||
const char time_separator = dos.tables.country[DOS_TIME_SEPARATOR_OFS];
|
||||
const char decimal_separator = dos.tables.country[DOS_DECIMAL_SEPARATOR_OFS];
|
||||
const char time_separator = DOS_GetLocaleTimeSeparator();
|
||||
const char decimal_separator = DOS_GetLocaleDecimalSeparator();
|
||||
if (full) // Example full time format: 1:02:03.04am
|
||||
safe_sprintf(return_time_buffer, "%u%c%02u%c%02u%c%02u%s",
|
||||
(unsigned int)fhour, time_separator,
|
||||
|
@ -1675,20 +1675,20 @@ void DOS_Shell::CMD_CALL(char * args){
|
|||
|
||||
void DOS_Shell::CMD_DATE(char *args)
|
||||
{
|
||||
const char date_format = dos.tables.country[DOS_DATE_FORMAT_OFS];
|
||||
const char date_separator = dos.tables.country[DOS_DATE_SEPARATOR_OFS];
|
||||
const auto date_format = DOS_GetLocaleDateFormat();
|
||||
const char date_separator = DOS_GetLocaleDateSeparator();
|
||||
char format[11];
|
||||
int result;
|
||||
switch (date_format) {
|
||||
case 1:
|
||||
case DosDateFormat::DayMonthYear:
|
||||
result = safe_sprintf(format, "DD%cMM%cYYYY", date_separator,
|
||||
date_separator);
|
||||
break;
|
||||
case 2:
|
||||
case DosDateFormat::YearMonthDay:
|
||||
result = safe_sprintf(format, "YYYY%cMM%cDD", date_separator,
|
||||
date_separator);
|
||||
break;
|
||||
default:
|
||||
default: // DosDateFormat::MonthDayYear
|
||||
result = safe_sprintf(format, "MM%cDD%cYYYY", date_separator,
|
||||
date_separator);
|
||||
}
|
||||
|
@ -1723,15 +1723,15 @@ void DOS_Shell::CMD_DATE(char *args)
|
|||
char date_separator_placeholder_1, date_separator_placeholder_2;
|
||||
int n;
|
||||
switch (date_format) {
|
||||
case 1:
|
||||
case DosDateFormat::DayMonthYear:
|
||||
n = sscanf(args, "%u%c%u%c%u", &newday, &date_separator_placeholder_1,
|
||||
&newmonth, &date_separator_placeholder_2, &newyear);
|
||||
break;
|
||||
case 2:
|
||||
case DosDateFormat::YearMonthDay:
|
||||
n = sscanf(args, "%u%c%u%c%u", &newyear, &date_separator_placeholder_1,
|
||||
&newmonth, &date_separator_placeholder_2, &newday);
|
||||
break;
|
||||
default:
|
||||
default: // DosDateFormat::MonthDayYear
|
||||
n = sscanf(args, "%u%c%u%c%u", &newmonth,
|
||||
&date_separator_placeholder_1, &newday,
|
||||
&date_separator_placeholder_2, &newyear);
|
||||
|
@ -1780,7 +1780,7 @@ void DOS_Shell::CMD_DATE(char *args)
|
|||
|
||||
void DOS_Shell::CMD_TIME(char * args) {
|
||||
char format[9], example[9];
|
||||
const char time_separator = dos.tables.country[DOS_TIME_SEPARATOR_OFS];
|
||||
const char time_separator = DOS_GetLocaleTimeSeparator();
|
||||
sprintf(format, "hh%cmm%css", time_separator, time_separator);
|
||||
sprintf(example, "13%c14%c15", time_separator, time_separator);
|
||||
if (ScanCMDBool(args, "?")) {
|
||||
|
|
|
@ -547,6 +547,7 @@ IF %ERRORLEVEL% LSS 8 SET ERRORLEVEL = 0</Command>
|
|||
<ClCompile Include="..\src\dos\dos_files.cpp" />
|
||||
<ClCompile Include="..\src\dos\dos_ioctl.cpp" />
|
||||
<ClCompile Include="..\src\dos\dos_keyboard_layout.cpp" />
|
||||
<ClCompile Include="..\src\dos\dos_locale.cpp" />
|
||||
<ClCompile Include="..\src\dos\dos_memory.cpp" />
|
||||
<ClCompile Include="..\src\dos\dos_misc.cpp" />
|
||||
<ClCompile Include="..\src\dos\dos_mscdex.cpp" />
|
||||
|
@ -855,6 +856,7 @@ IF %ERRORLEVEL% LSS 8 SET ERRORLEVEL = 0</Command>
|
|||
<ClInclude Include="..\src\debug\debug_inc.h" />
|
||||
<ClInclude Include="..\src\dos\cdrom.h" />
|
||||
<ClInclude Include="..\src\dos\dev_con.h" />
|
||||
<ClInclude Include="..\src\dos\dos_locale.h" />
|
||||
<ClInclude Include="..\src\dos\dos_mscdex.h" />
|
||||
<ClInclude Include="..\src\dos\program_autotype.h" />
|
||||
<ClInclude Include="..\src\dos\program_ls.h" />
|
||||
|
|
|
@ -97,6 +97,9 @@
|
|||
<ClCompile Include="..\src\dos\dos_keyboard_layout.cpp">
|
||||
<Filter>src\dos</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\dos\dos_locale.cpp">
|
||||
<Filter>src\dos</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\dos\dos_memory.cpp">
|
||||
<Filter>src\dos</Filter>
|
||||
</ClCompile>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue