DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dos_keyboard_layout.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include "dosbox.h"
00021 #include "bios.h"
00022 #include "bios_disk.h"
00023 #include "setup.h"
00024 #include "support.h"
00025 #include "../ints/int10.h"
00026 #include "regs.h"
00027 #include "callback.h"
00028 #include "mapper.h"
00029 #include "drives.h"
00030 #include "dos_inc.h"
00031 #include "control.h"
00032 
00033 #include "dos_codepages.h"
00034 #include "dos_keyboard_layout_data.h"
00035 
00036 #if defined (WIN32)
00037 #include <windows.h>
00038 #endif
00039 
00040 
00041 static FILE* OpenDosboxFile(const char* name) {
00042         Bit8u drive;
00043         char fullname[DOS_PATHLENGTH];
00044 
00045         localDrive* ldp=0;
00046         // try to build dos name
00047         if (DOS_MakeName(name,fullname,&drive)) {
00048                 try {
00049                         // try to open file on mounted drive first
00050                         ldp=dynamic_cast<localDrive*>(Drives[drive]);
00051                         if (ldp) {
00052                                 FILE *tmpfile=ldp->GetSystemFilePtr(fullname, "rb");
00053                                 if (tmpfile != NULL) return tmpfile;
00054                         }
00055                 }
00056                 catch(...) {}
00057         }
00058         FILE *tmpfile=fopen(name, "rb");
00059         return tmpfile;
00060 }
00061 
00062 
00063 class keyboard_layout {
00064 public:
00065         keyboard_layout() {
00066                 this->reset();
00067                 language_codes=NULL;
00068                 use_foreign_layout=false;
00069                 sprintf(current_keyboard_file_name, "none");
00070         };
00071 
00072         ~keyboard_layout();
00073 
00074         // read in a codepage from a .cpi-file
00075         Bitu read_codepage_file(const char* codepage_file_name, Bit32s codepage_id);
00076         Bit16u extract_codepage(const char* keyboard_file_name);
00077         // read in a keyboard layout from a .kl-file
00078         Bitu read_keyboard_file(const char* keyboard_file_name, Bit32s req_cp);
00079 
00080         // call layout_key to apply the current language layout
00081         bool layout_key(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3);
00082 
00083         Bitu switch_keyboard_layout(const char* new_layout, keyboard_layout* &created_layout, Bit32s& tried_cp);
00084         void switch_foreign_layout();
00085         const char* get_layout_name();
00086         const char* main_language_code();
00087 
00088 
00089 private:
00090         static const Bit8u layout_pages=12;
00091         Bit16u current_layout[(MAX_SCAN_CODE+1)*layout_pages];
00092         struct {
00093                 Bit16u required_flags,forbidden_flags;
00094                 Bit16u required_userflags,forbidden_userflags;
00095         } current_layout_planes[layout_pages-4];
00096     Bit8u additional_planes = 0;
00097     Bit8u used_lock_modifiers;
00098 
00099         // diacritics table
00100     Bit8u diacritics[2048] = {};
00101         Bit16u diacritics_entries;
00102         Bit16u diacritics_character;
00103         Bit16u user_keys;
00104 
00105         char current_keyboard_file_name[256];
00106         bool use_foreign_layout;
00107 
00108         // language code storage used when switching layouts
00109         char** language_codes;
00110         Bitu language_code_count;
00111 
00112         void reset();
00113         void read_keyboard_file(Bit32s specific_layout);
00114         Bitu read_keyboard_file(const char* keyboard_file_name, Bit32s specific_layout, Bit32s requested_codepage);
00115         bool map_key(Bitu key, Bit16u layouted_key, bool is_command, bool is_keypair);
00116 };
00117 
00118 
00119 keyboard_layout::~keyboard_layout() {
00120         if (language_codes) {
00121                 for (Bitu i=0; i<language_code_count; i++)
00122                         delete[] language_codes[i];
00123                 delete[] language_codes;
00124                 language_codes=NULL;
00125         }
00126 }
00127 
00128 void keyboard_layout::reset() {
00129         for (Bit32u i=0; i<(MAX_SCAN_CODE+1)*layout_pages; i++) current_layout[i]=0;
00130         for (Bit32u i=0; i<layout_pages-4; i++) {
00131                 current_layout_planes[i].required_flags=0;
00132                 current_layout_planes[i].forbidden_flags=0xffff;
00133                 current_layout_planes[i].required_userflags=0;
00134                 current_layout_planes[i].forbidden_userflags=0xffff;
00135         }
00136         used_lock_modifiers=0x0f;
00137         diacritics_entries=0;           // no diacritics loaded
00138         diacritics_character=0;
00139         user_keys=0;                            // all userkeys off
00140         language_code_count=0;
00141 }
00142 
00143 Bitu keyboard_layout::read_keyboard_file(const char* keyboard_file_name, Bit32s req_cp) {
00144         return this->read_keyboard_file(keyboard_file_name, -1, req_cp);
00145 }
00146 
00147 // switch to a different layout
00148 void keyboard_layout::read_keyboard_file(Bit32s specific_layout) {
00149         if (strcmp(current_keyboard_file_name,"none"))
00150                 this->read_keyboard_file(current_keyboard_file_name, specific_layout, dos.loaded_codepage);
00151 }
00152 
00153 static Bit32u read_kcl_file(const char* kcl_file_name, const char* layout_id, bool first_id_only) {
00154         FILE* tempfile = OpenDosboxFile(kcl_file_name);
00155         if (tempfile==0) return 0;
00156 
00157         static Bit8u rbuf[8192];
00158 
00159         // check ID-bytes of file
00160         Bit32u dr=(Bit32u)fread(rbuf, sizeof(Bit8u), 7, tempfile);
00161         if ((dr<7) || (rbuf[0]!=0x4b) || (rbuf[1]!=0x43) || (rbuf[2]!=0x46)) {
00162                 fclose(tempfile);
00163                 return 0;
00164         }
00165 
00166         fseek(tempfile, 7+rbuf[6], SEEK_SET);
00167 
00168         for (;;) {
00169                 Bit32u cur_pos=(Bit32u)(ftell(tempfile));
00170                 dr=(Bit32u)fread(rbuf, sizeof(Bit8u), 5, tempfile);
00171                 if (dr<5) break;
00172                 Bit16u len=host_readw(&rbuf[0]);
00173 
00174                 Bit8u data_len=rbuf[2];
00175 
00176                 char lng_codes[258];
00177                 fseek(tempfile, -2, SEEK_CUR);
00178                 // get all language codes for this layout
00179                 for (Bitu i=0; i<data_len;) {
00180             size_t readResult = fread(rbuf, sizeof(Bit8u), 2, tempfile);
00181             if (readResult != 2) {
00182                 LOG(LOG_IO, LOG_ERROR) ("Reading error in read_kcl_file\n");
00183                 return 0;
00184             }
00185                         Bit16u lcnum=host_readw(&rbuf[0]);
00186                         i+=2;
00187                         Bitu lcpos=0;
00188                         for (;i<data_len;) {
00189                 readResult = fread(rbuf, sizeof(Bit8u), 1, tempfile);
00190                 if (readResult != 1) {
00191                     LOG(LOG_IO, LOG_ERROR) ("Reading error in read_kcl_file\n");
00192                     return 0;
00193                 }
00194                                 i++;
00195                                 if (((char)rbuf[0])==',') break;
00196                                 lng_codes[lcpos++]=(char)rbuf[0];
00197                         }
00198                         lng_codes[lcpos]=0;
00199                         if (strcasecmp(lng_codes, layout_id)==0) {
00200                                 // language ID found in file, return file position
00201                                 fclose(tempfile);
00202                                 return cur_pos;
00203                         }
00204                         if (first_id_only) break;
00205                         if (lcnum) {
00206                                 sprintf(&lng_codes[lcpos],"%d",lcnum);
00207                                 if (strcasecmp(lng_codes, layout_id)==0) {
00208                                         // language ID found in file, return file position
00209                                         return cur_pos;
00210                                 }
00211                         }
00212                 }
00213                 fseek(tempfile, long(cur_pos+3+len), SEEK_SET);
00214         }
00215 
00216         fclose(tempfile);
00217         return 0;
00218 }
00219 
00220 static Bit32u read_kcl_data(const Bit8u* kcl_data, Bit32u kcl_data_size, const char* layout_id, bool first_id_only) {
00221         // check ID-bytes
00222         if ((kcl_data[0]!=0x4b) || (kcl_data[1]!=0x43) || (kcl_data[2]!=0x46)) {
00223                 return 0;
00224         }
00225 
00226         Bit32u dpos=7u+kcl_data[6];
00227 
00228         for (;;) {
00229                 if (dpos+5>kcl_data_size) break;
00230                 Bit32u cur_pos=dpos;
00231                 Bit16u len=host_readw(&kcl_data[dpos]);
00232                 Bit8u data_len=kcl_data[dpos+2];
00233                 dpos+=5;
00234 
00235                 char lng_codes[258];
00236                 // get all language codes for this layout
00237                 for (Bitu i=0; i<data_len;) {
00238                         Bit16u lcnum=host_readw(&kcl_data[dpos-2]);
00239                         i+=2;
00240                         Bitu lcpos=0;
00241                         for (;i<data_len;) {
00242                                 if (dpos+1>kcl_data_size) break;
00243                                 char lc=(char)kcl_data[dpos];
00244                                 dpos++;
00245                                 i++;
00246                                 if (lc==',') break;
00247                                 lng_codes[lcpos++]=lc;
00248                         }
00249                         lng_codes[lcpos]=0;
00250                         if (strcasecmp(lng_codes, layout_id)==0) {
00251                                 // language ID found, return position
00252                                 return cur_pos;
00253                         }
00254                         if (first_id_only) break;
00255                         if (lcnum) {
00256                                 sprintf(&lng_codes[lcpos],"%d",lcnum);
00257                                 if (strcasecmp(lng_codes, layout_id)==0) {
00258                                         // language ID found, return position
00259                                         return cur_pos;
00260                                 }
00261                         }
00262                         dpos+=2;
00263                 }
00264                 dpos=cur_pos+3+len;
00265         }
00266         return 0;
00267 }
00268 
00269 Bitu keyboard_layout::read_keyboard_file(const char* keyboard_file_name, Bit32s specific_layout, Bit32s requested_codepage) {
00270         this->reset();
00271 
00272         if (specific_layout==-1) strcpy(current_keyboard_file_name, keyboard_file_name);
00273         if (!strcmp(keyboard_file_name,"none")) return KEYB_NOERROR;
00274 
00275         static Bit8u read_buf[65535];
00276         Bit32u read_buf_size, read_buf_pos, bytes_read;
00277         Bit32u start_pos=5;
00278 
00279         char nbuf[512];
00280         read_buf_size = 0;
00281         sprintf(nbuf, "%s.kl", keyboard_file_name);
00282         FILE* tempfile = OpenDosboxFile(nbuf);
00283         if (tempfile==NULL) {
00284                 // try keyboard layout libraries next
00285                 if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,true))) {
00286                         tempfile = OpenDosboxFile("keyboard.sys");
00287                 } else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,true))) {
00288                         tempfile = OpenDosboxFile("keybrd2.sys");
00289                 } else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,true))) {
00290                         tempfile = OpenDosboxFile("keybrd3.sys");
00291                 } else if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,false))) {
00292                         tempfile = OpenDosboxFile("keyboard.sys");
00293                 } else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,false))) {
00294                         tempfile = OpenDosboxFile("keybrd2.sys");
00295                 } else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,false))) {
00296                         tempfile = OpenDosboxFile("keybrd3.sys");
00297                 } else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,true))) {
00298                         read_buf_size=0;
00299                         for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
00300                 } else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,true))) {
00301                         read_buf_size=0;
00302                         for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
00303                 } else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,true))) {
00304                         read_buf_size=0;
00305                         for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
00306                 } else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,false))) {
00307                         read_buf_size=0;
00308                         for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
00309                 } else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,false))) {
00310                         read_buf_size=0;
00311                         for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
00312                 } else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,false))) {
00313                         read_buf_size=0;
00314                         for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
00315                 } else {
00316                         LOG(LOG_BIOS,LOG_ERROR)("Keyboard layout file %s not found",keyboard_file_name);
00317                         return KEYB_FILENOTFOUND;
00318                 }
00319                 if (tempfile) {
00320                         fseek(tempfile, long(start_pos+2), SEEK_SET);
00321                         read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
00322                         fclose(tempfile);
00323                 }
00324                 start_pos=0;
00325         } else {
00326                 // check ID-bytes of file
00327                 Bit32u dr=(Bit32u)fread(read_buf, sizeof(Bit8u), 4, tempfile);
00328                 if ((dr<4) || (read_buf[0]!=0x4b) || (read_buf[1]!=0x4c) || (read_buf[2]!=0x46)) {
00329                         LOG(LOG_BIOS,LOG_ERROR)("Invalid keyboard layout file %s",keyboard_file_name);
00330                         return KEYB_INVALIDFILE;
00331                 }
00332                 
00333                 fseek(tempfile, 0, SEEK_SET);
00334                 read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
00335                 fclose(tempfile);
00336         }
00337 
00338         Bit8u data_len,submappings;
00339         data_len=read_buf[start_pos++];
00340 
00341         language_codes=new char*[data_len];
00342         language_code_count=0;
00343         // get all language codes for this layout
00344         for (Bitu i=0; i<data_len;) {
00345                 language_codes[language_code_count]=new char[256];
00346                 i+=2;
00347                 Bitu lcpos=0;
00348                 for (;i<data_len;) {
00349                         char lcode=char(read_buf[start_pos+i]);
00350                         i++;
00351                         if (lcode==',') break;
00352                         language_codes[language_code_count][lcpos++]=lcode;
00353                 }
00354                 language_codes[language_code_count][lcpos]=0;
00355                 language_code_count++;
00356         }
00357 
00358         start_pos+=data_len;            // start_pos==absolute position of KeybCB block
00359 
00360         submappings=read_buf[start_pos];
00361         additional_planes=read_buf[start_pos+1];
00362 
00363         // four pages always occupied by normal,shift,flags,commandbits
00364         if (additional_planes>(layout_pages-4)) additional_planes=(layout_pages-4);
00365 
00366         // seek to plane descriptor
00367         read_buf_pos=start_pos+0x14u+submappings*8u;
00368         for (Bit16u cplane=0; cplane<additional_planes; cplane++) {
00369                 Bit16u plane_flags;
00370 
00371                 // get required-flags (shift/alt/ctrl-states etc.)
00372                 plane_flags=host_readw(&read_buf[read_buf_pos]);
00373                 read_buf_pos+=2;
00374                 current_layout_planes[cplane].required_flags=plane_flags;
00375                 used_lock_modifiers |= (plane_flags&0x70);
00376                 // get forbidden-flags
00377                 plane_flags=host_readw(&read_buf[read_buf_pos]);
00378                 read_buf_pos+=2;
00379                 current_layout_planes[cplane].forbidden_flags=plane_flags;
00380 
00381                 // get required-userflags
00382                 plane_flags=host_readw(&read_buf[read_buf_pos]);
00383                 read_buf_pos+=2;
00384                 current_layout_planes[cplane].required_userflags=plane_flags;
00385                 // get forbidden-userflags
00386                 plane_flags=host_readw(&read_buf[read_buf_pos]);
00387                 read_buf_pos+=2;
00388                 current_layout_planes[cplane].forbidden_userflags=plane_flags;
00389         }
00390 
00391         bool found_matching_layout=false;
00392         
00393         // check all submappings and use them if general submapping or same codepage submapping
00394         for (Bit16u sub_map=0; (sub_map<submappings) && (!found_matching_layout); sub_map++) {
00395                 Bit16u submap_cp, table_offset;
00396 
00397                 if ((sub_map!=0) && (specific_layout!=-1)) sub_map=(Bit16u)(specific_layout&0xffff);
00398 
00399                 // read codepage of submapping
00400                 submap_cp=host_readw(&read_buf[start_pos+0x14u+sub_map*8u]);
00401                 if ((submap_cp!=0) && (submap_cp!=requested_codepage) && (specific_layout==-1))
00402                         continue;               // skip nonfitting submappings
00403 
00404                 if (submap_cp==requested_codepage) found_matching_layout=true;
00405 
00406                 // get table offset
00407                 table_offset=host_readw(&read_buf[start_pos+0x18u+sub_map*8u]);
00408                 diacritics_entries=0;
00409                 if (table_offset!=0) {
00410                         // process table
00411                         Bit16u i,j;
00412                         for (i=0; i<2048;) {
00413                                 if (read_buf[start_pos+table_offset+i]==0) break;       // end of table
00414                                 diacritics_entries++;
00415                                 i+=read_buf[start_pos+table_offset+i+1]*2+2;
00416                         }
00417                         // copy diacritics table
00418                         for (j=0; j<=i; j++) diacritics[j]=read_buf[start_pos+table_offset+j];
00419                 }
00420 
00421 
00422                 // get table offset
00423                 table_offset=host_readw(&read_buf[start_pos+0x16u+sub_map*8u]);
00424                 if (table_offset==0) continue;  // non-present table
00425 
00426                 read_buf_pos=start_pos+table_offset;
00427 
00428                 bytes_read=read_buf_size-read_buf_pos;
00429 
00430                 // process submapping table
00431                 for (Bit32u i=0; i<bytes_read;) {
00432                         Bit8u scan=read_buf[read_buf_pos++];
00433                         if (scan==0) break;
00434                         Bit8u scan_length=(read_buf[read_buf_pos]&7)+1;         // length of data struct
00435                         read_buf_pos+=2;
00436                         i+=3;
00437                         if (((scan&0x7f)<=MAX_SCAN_CODE) && (scan_length>0)) {
00438                                 // add all available mappings
00439                                 for (Bit16u addmap=0; addmap<scan_length; addmap++) {
00440                                         if (addmap>additional_planes+2) break;
00441                                         Bitu charptr=read_buf_pos+addmap*((read_buf[read_buf_pos-2u]&0x80u)?2u:1u);
00442                                         Bit16u kchar=read_buf[charptr];
00443 
00444                                         if (kchar!=0) {         // key remapped
00445                                                 if (read_buf[read_buf_pos-2]&0x80) kchar|=read_buf[charptr+1]<<8;       // scancode/char pair
00446                                                 // overwrite mapping
00447                                                 current_layout[scan*layout_pages+addmap]=kchar;
00448                                                 // clear command bit
00449                                                 current_layout[scan*layout_pages+layout_pages-2]&=~(1<<addmap);
00450                                                 // add command bit
00451                                                 current_layout[scan*layout_pages+layout_pages-2]|=(read_buf[read_buf_pos-1] & (1<<addmap));
00452                                         }
00453                                 }
00454 
00455                                 // calculate max length of entries, taking into account old number of entries
00456                                 Bit8u new_flags=current_layout[scan*layout_pages+layout_pages-1]&0x7;
00457                                 if ((read_buf[read_buf_pos-2]&0x7) > new_flags) new_flags = read_buf[read_buf_pos-2]&0x7;
00458 
00459                                 // merge flag bits in as well
00460                                 new_flags |= (read_buf[read_buf_pos-2] | current_layout[scan*layout_pages+layout_pages-1]) & 0xf0;
00461 
00462                                 current_layout[scan*layout_pages+layout_pages-1]=new_flags;
00463                                 if (read_buf[read_buf_pos-2]&0x80) scan_length*=2;              // granularity flag (S)
00464                         }
00465                         i+=scan_length;         // advance pointer
00466                         read_buf_pos+=scan_length;
00467                 }
00468                 if (specific_layout==sub_map) break;
00469         }
00470 
00471         if (found_matching_layout) {
00472                 if (specific_layout==-1) LOG(LOG_BIOS,LOG_NORMAL)("Keyboard layout %s successfully loaded",keyboard_file_name);
00473                 else LOG(LOG_BIOS,LOG_NORMAL)("Keyboard layout %s (%i) successfully loaded",keyboard_file_name,specific_layout);
00474                 this->use_foreign_layout=true;
00475                 return KEYB_NOERROR;
00476         }
00477 
00478         LOG(LOG_BIOS,LOG_ERROR)("No matching keyboard layout found in %s",keyboard_file_name);
00479 
00480         // reset layout data (might have been changed by general layout)
00481         this->reset();
00482 
00483         return KEYB_LAYOUTNOTFOUND;
00484 }
00485 
00486 bool keyboard_layout::layout_key(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3) {
00487         if (key>MAX_SCAN_CODE) return false;
00488         if (!this->use_foreign_layout) return false;
00489 
00490         bool is_special_pair=(current_layout[key*layout_pages+layout_pages-1] & 0x80)==0x80;
00491 
00492         if ((((flags1&used_lock_modifiers)&0x7c)==0) && ((flags3&2)==0)) {
00493                 // check if shift/caps is active:
00494                 // (left_shift OR right_shift) XOR (key_affected_by_caps AND caps_locked)
00495                 if ((((flags1&2)>>1) | (flags1&1)) ^ (((current_layout[key*layout_pages+layout_pages-1] & 0x40) & (flags1 & 0x40))>>6)) {
00496                         // shift plane
00497                         if (current_layout[key*layout_pages+1]!=0) {
00498                                 // check if command-bit is set for shift plane
00499                                 bool is_command=(current_layout[key*layout_pages+layout_pages-2]&2)!=0;
00500                                 if (this->map_key(key, current_layout[key*layout_pages+1],
00501                                         is_command, is_special_pair)) return true;
00502                         }
00503                 } else {
00504                         // normal plane
00505                         if (current_layout[key*layout_pages]!=0) {
00506                                 // check if command-bit is set for normal plane
00507                                 bool is_command=(current_layout[key*layout_pages+layout_pages-2]&1)!=0;
00508                                 if (this->map_key(key, current_layout[key*layout_pages],
00509                                         is_command, is_special_pair)) return true;
00510                         }
00511                 }
00512         }
00513 
00514         // calculate current flags
00515         Bit16u current_flags=(flags1&0x7f) | (((flags2&3) | (flags3&0xc))<<8);
00516         if (flags1&3) current_flags|=0x4000;    // either shift key active
00517         if (flags3&2) current_flags|=0x1000;    // e0 prefixed
00518 
00519         // check all planes if flags fit
00520         for (Bit16u cplane=0; cplane<additional_planes; cplane++) {
00521                 Bit16u req_flags=current_layout_planes[cplane].required_flags;
00522                 Bit16u req_userflags=current_layout_planes[cplane].required_userflags;
00523                 // test flags
00524                 if (((current_flags & req_flags)==req_flags) &&
00525                         ((user_keys & req_userflags)==req_userflags) &&
00526                         ((current_flags & current_layout_planes[cplane].forbidden_flags)==0) &&
00527                         ((user_keys & current_layout_planes[cplane].forbidden_userflags)==0)) {
00528                                 // remap key
00529                                 if (current_layout[key*layout_pages+2+cplane]!=0) {
00530                                         // check if command-bit is set for this plane
00531                                         bool is_command=((current_layout[key*layout_pages+layout_pages-2]>>(cplane+2))&1)!=0;
00532                                         if (this->map_key(key, current_layout[key*layout_pages+2+cplane],
00533                                                 is_command, is_special_pair)) return true;
00534                                 } else break;   // abort plane checking
00535                         }
00536         }
00537 
00538         if (diacritics_character>0) {
00539                 // ignore state-changing keys
00540                 switch(key) {
00541                         case 0x1d:                      /* Ctrl Pressed */
00542                         case 0x2a:                      /* Left Shift Pressed */
00543                         case 0x36:                      /* Right Shift Pressed */
00544                         case 0x38:                      /* Alt Pressed */
00545                         case 0x3a:                      /* Caps Lock */
00546                         case 0x45:                      /* Num Lock */
00547                         case 0x46:                      /* Scroll Lock */
00548                                 break;
00549                         default:
00550                                 if (diacritics_character-200>=diacritics_entries) {
00551                                         diacritics_character=0;
00552                                         return true;
00553                                 }
00554                                 Bit16u diacritics_start=0;
00555                                 // search start of subtable
00556                                 for (Bit16u i=0; i<diacritics_character-200; i++)
00557                                         diacritics_start+=diacritics[diacritics_start+1]*2+2;
00558 
00559                                 BIOS_AddKeyToBuffer((Bit16u)(key<<8) | diacritics[diacritics_start]);
00560                                 diacritics_character=0;
00561                 }
00562         }
00563 
00564         return false;
00565 }
00566 
00567 bool keyboard_layout::map_key(Bitu key, Bit16u layouted_key, bool is_command, bool is_keypair) {
00568         if (is_command) {
00569                 Bit8u key_command=(Bit8u)(layouted_key&0xff);
00570                 // check if diacritics-command
00571                 if ((key_command>=200) && (key_command<235)) {
00572                         // diacritics command
00573                         diacritics_character=key_command;
00574                         if (diacritics_character-200>=diacritics_entries) diacritics_character=0;
00575                         return true;
00576                 } else if ((key_command>=120) && (key_command<140)) {
00577                         // switch layout command
00578                         this->read_keyboard_file(key_command-119);
00579                         return true;
00580                 } else if ((key_command>=180) && (key_command<188)) {
00581                         // switch user key off
00582                         user_keys&=~(1<<(key_command-180));
00583                         return true;
00584                 } else if ((key_command>=188) && (key_command<196)) {
00585                         // switch user key on
00586                         user_keys|=(1<<(key_command-188));
00587                         return true;
00588                 } else if (key_command==160) return true;       // nop command
00589         } else {
00590                 // non-command
00591                 if (diacritics_character>0) {
00592                         if (diacritics_character-200>=diacritics_entries) diacritics_character = 0;
00593                         else {
00594                                 Bit16u diacritics_start=0;
00595                                 // search start of subtable
00596                                 for (Bit16u i=0; i<diacritics_character-200; i++)
00597                                         diacritics_start+=diacritics[diacritics_start+1]*2+2;
00598 
00599                                 Bit8u diacritics_length=diacritics[diacritics_start+1];
00600                                 diacritics_start+=2;
00601                                 diacritics_character=0; // reset
00602 
00603                                 // search scancode
00604                                 for (Bit16u i=0; i<diacritics_length; i++) {
00605                                         if (diacritics[diacritics_start+i*2]==(layouted_key&0xff)) {
00606                                                 // add diacritics to keybuf
00607                                                 BIOS_AddKeyToBuffer((Bit16u)(key<<8) | diacritics[diacritics_start+i*2+1]);
00608                                                 return true;
00609                                         }
00610                                 }
00611                                 // add standard-diacritics to keybuf
00612                                 BIOS_AddKeyToBuffer((Bit16u)(key<<8) | diacritics[diacritics_start-2]);
00613                         }
00614                 }
00615 
00616                 // add remapped key to keybuf
00617                 if (is_keypair) BIOS_AddKeyToBuffer(layouted_key);
00618                 else BIOS_AddKeyToBuffer((Bit16u)(key<<8) | (layouted_key&0xff));
00619 
00620                 return true;
00621         }
00622         return false;
00623 }
00624 
00625 Bit16u keyboard_layout::extract_codepage(const char* keyboard_file_name) {
00626         if (!strcmp(keyboard_file_name,"none")) return (IS_PC98_ARCH ? 932 : 437);
00627 
00628         Bit32u read_buf_size;
00629         static Bit8u read_buf[65535];
00630         Bit32u start_pos=5;
00631 
00632         char nbuf[512];
00633         sprintf(nbuf, "%s.kl", keyboard_file_name);
00634         FILE* tempfile = OpenDosboxFile(nbuf);
00635         if (tempfile==NULL) {
00636                 // try keyboard layout libraries next
00637                 if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,true))) {
00638                         tempfile = OpenDosboxFile("keyboard.sys");
00639                 } else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,true))) {
00640                         tempfile = OpenDosboxFile("keybrd2.sys");
00641                 } else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,true))) {
00642                         tempfile = OpenDosboxFile("keybrd3.sys");
00643                 } else if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,false))) {
00644                         tempfile = OpenDosboxFile("keyboard.sys");
00645                 } else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,false))) {
00646                         tempfile = OpenDosboxFile("keybrd2.sys");
00647                 } else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,false))) {
00648                         tempfile = OpenDosboxFile("keybrd3.sys");
00649                 } else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,true))) {
00650                         read_buf_size=0;
00651                         for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
00652                 } else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,true))) {
00653                         read_buf_size=0;
00654                         for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
00655                 } else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,true))) {
00656                         read_buf_size=0;
00657                         for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
00658                 } else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,false))) {
00659                         read_buf_size=0;
00660                         for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
00661                 } else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,false))) {
00662                         read_buf_size=0;
00663                         for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
00664                 } else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,false))) {
00665                         read_buf_size=0;
00666                         for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
00667                 } else {
00668                         start_pos=0;
00669                         LOG(LOG_BIOS,LOG_ERROR)("Keyboard layout file %s not found",keyboard_file_name);
00670                         return (IS_PC98_ARCH ? 932 : 437);
00671                 }
00672                 if (tempfile) {
00673                         fseek(tempfile, long(start_pos+2), SEEK_SET);
00674                         read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
00675                         fclose(tempfile);
00676                 }
00677                 start_pos=0;
00678         } else {
00679                 // check ID-bytes of file
00680                 Bit32u dr=(Bit32u)fread(read_buf, sizeof(Bit8u), 4, tempfile);
00681                 if ((dr<4) || (read_buf[0]!=0x4b) || (read_buf[1]!=0x4c) || (read_buf[2]!=0x46)) {
00682                         LOG(LOG_BIOS,LOG_ERROR)("Invalid keyboard layout file %s",keyboard_file_name);
00683                         return (IS_PC98_ARCH ? 932 : 437);
00684                 }
00685 
00686                 fseek(tempfile, 0, SEEK_SET);
00687                 read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
00688                 fclose(tempfile);
00689         }
00690 
00691         Bit8u data_len,submappings;
00692         data_len=read_buf[start_pos++];
00693 
00694         start_pos+=data_len;            // start_pos==absolute position of KeybCB block
00695 
00696         submappings=read_buf[start_pos];
00697 
00698         // check all submappings and use them if general submapping or same codepage submapping
00699         for (Bit16u sub_map=0; (sub_map<submappings); sub_map++) {
00700                 Bit16u submap_cp;
00701 
00702                 // read codepage of submapping
00703                 submap_cp=host_readw(&read_buf[start_pos+0x14u+sub_map*8u]);
00704 
00705                 if (submap_cp!=0) return submap_cp;
00706         }
00707         return (IS_PC98_ARCH ? 932 : 437);
00708 }
00709 
00710 Bitu keyboard_layout::read_codepage_file(const char* codepage_file_name, Bit32s codepage_id) {
00711         char cp_filename[512];
00712         strcpy(cp_filename, codepage_file_name);
00713         if (!strcmp(cp_filename,"none")) return KEYB_NOERROR;
00714 
00715         if (codepage_id==dos.loaded_codepage) return KEYB_NOERROR;
00716 
00717         if (!strcmp(cp_filename,"auto")) {
00718                 // select matching .cpi-file for specified codepage
00719                 switch (codepage_id) {
00720                         case 437:       case 850:       case 852:       case 853:       case 857:       case 858:       
00721                                                 sprintf(cp_filename, "EGA.CPI"); break;
00722                         case 775:       case 859:       case 1116:      case 1117:
00723                                                 sprintf(cp_filename, "EGA2.CPI"); break;
00724                         case 771:       case 772:       case 808:       case 855:       case 866:       case 872:
00725                                                 sprintf(cp_filename, "EGA3.CPI"); break;
00726                         case 848:       case 849:       case 1125:      case 1131:      case 61282:
00727                                                 sprintf(cp_filename, "EGA4.CPI"); break;
00728                         case 737:       case 851:       case 869:
00729                                                 sprintf(cp_filename, "EGA5.CPI"); break;
00730                         case 113:       case 899:       case 59829:     case 60853:
00731                                                 sprintf(cp_filename, "EGA6.CPI"); break;
00732                         case 58152:     case 58210:     case 59234:     case 60258:     case 62306:
00733                                                 sprintf(cp_filename, "EGA7.CPI"); break;
00734                         case 770:       case 773:       case 774:       case 777:       case 778:
00735                                                 sprintf(cp_filename, "EGA8.CPI"); break;
00736                         case 860:       case 861:       case 863:       case 865:
00737                                                 sprintf(cp_filename, "EGA9.CPI"); break;
00738                         case 667:       case 668:       case 790:       case 867:       case 991:       case 57781:
00739                                                 sprintf(cp_filename, "EGA10.CPI"); break;
00740                         default:
00741                                 LOG_MSG("No matching cpi file for codepage %i",codepage_id);
00742                                 return KEYB_INVALIDCPFILE;
00743                 }
00744         }
00745 
00746         Bit32u start_pos;
00747         Bit16u number_of_codepages;
00748 
00749         char nbuf[512];
00750         sprintf(nbuf, "%s", cp_filename);
00751         FILE* tempfile=OpenDosboxFile(nbuf);
00752         if (tempfile==NULL) {
00753                 size_t strsz=strlen(nbuf);
00754                 if (strsz) {
00755                         char plc=(char)toupper(*reinterpret_cast<unsigned char*>(&nbuf[strsz-1]));
00756                         if (plc=='I') {
00757                                 // try CPX-extension as well
00758                                 nbuf[strsz-1]='X';
00759                                 tempfile=OpenDosboxFile(nbuf);
00760                         } else if (plc=='X') {
00761                                 // try CPI-extension as well
00762                                 nbuf[strsz-1]='I';
00763                                 tempfile=OpenDosboxFile(nbuf);
00764                         }
00765                 }
00766         }
00767 
00768         static Bit8u cpi_buf[65536];
00769         Bit32u cpi_buf_size=0,size_of_cpxdata=0;
00770         bool upxfound=false;
00771         Bit16u found_at_pos=5;
00772         if (tempfile==NULL) {
00773                 // check if build-in codepage is available
00774                 switch (codepage_id) {
00775                         case 437:       case 850:       case 852:       case 853:       case 857:       case 858:       
00776                                                 for (Bitu bct=0; bct<6322; bct++) cpi_buf[bct]=font_ega_cpx[bct];
00777                                                 cpi_buf_size=6322;
00778                                                 break;
00779                         case 771:       case 772:       case 808:       case 855:       case 866:       case 872:
00780                                                 for (Bitu bct=0; bct<5455; bct++) cpi_buf[bct]=font_ega3_cpx[bct];
00781                                                 cpi_buf_size=5455;
00782                                                 break;
00783                         case 737:       case 851:       case 869:
00784                                                 for (Bitu bct=0; bct<5720; bct++) cpi_buf[bct]=font_ega5_cpx[bct];
00785                                                 cpi_buf_size=5720;
00786                                                 break;
00787                         default: 
00788                                 return KEYB_INVALIDCPFILE;
00789                 }
00790                 upxfound=true;
00791                 found_at_pos=0x29;
00792                 size_of_cpxdata=cpi_buf_size;
00793         } else {
00794                 Bit32u dr=(Bit32u)fread(cpi_buf, sizeof(Bit8u), 5, tempfile);
00795                 // check if file is valid
00796                 if (dr<5) {
00797                         LOG(LOG_BIOS,LOG_ERROR)("Codepage file %s invalid",cp_filename);
00798                         return KEYB_INVALIDCPFILE;
00799                 }
00800                 // check if non-compressed cpi file
00801                 if ((cpi_buf[0]!=0xff) || (cpi_buf[1]!=0x46) || (cpi_buf[2]!=0x4f) || 
00802                         (cpi_buf[3]!=0x4e) || (cpi_buf[4]!=0x54)) {
00803                         // check if dr-dos custom cpi file
00804                         if ((cpi_buf[0]==0x7f) && (cpi_buf[1]!=0x44) && (cpi_buf[2]!=0x52) && 
00805                                 (cpi_buf[3]!=0x46) && (cpi_buf[4]!=0x5f)) {
00806                                 LOG(LOG_BIOS,LOG_ERROR)("Codepage file %s has unsupported DR-DOS format",cp_filename);
00807                                 return KEYB_INVALIDCPFILE;
00808                         }
00809                         // check if compressed cpi file
00810                         Bit8u next_byte=0;
00811                         for (Bitu i=0; i<100; i++) {
00812                 size_t readResult = fread(&next_byte, sizeof(Bit8u), 1, tempfile);
00813                 if (readResult != 1) {
00814                     LOG(LOG_IO, LOG_ERROR) ("Reading error in read_codepage_file\n");
00815                 }
00816                 found_at_pos++;
00817                 while (next_byte == 0x55) {
00818                     readResult = fread(&next_byte, sizeof(Bit8u), 1, tempfile);
00819                     if (readResult != 1) {
00820                         LOG(LOG_IO, LOG_ERROR) ("Reading error in read_codepage_file\n");
00821                     }
00822                     found_at_pos++;
00823                     if (next_byte == 0x50) {
00824                         readResult = fread(&next_byte, sizeof(Bit8u), 1, tempfile);
00825                         if (readResult != 1) {
00826                             LOG(LOG_IO, LOG_ERROR) ("Reading error in read_codepage_file\n");
00827                         }
00828                         found_at_pos++;
00829                         if (next_byte == 0x58) {
00830                             readResult = fread(&next_byte, sizeof(Bit8u), 1, tempfile);
00831                             if (readResult != 1) {
00832                                 LOG(LOG_IO, LOG_ERROR) ("Reading error in read_codepage_file\n");
00833                             }
00834                             found_at_pos++;
00835                             if (next_byte == 0x21) {
00836                                 // read version ID
00837                                 readResult = fread(&next_byte, sizeof(Bit8u), 1, tempfile);
00838                                 if (readResult != 1) {
00839                                     LOG(LOG_IO, LOG_ERROR) ("Reading error in read_codepage_file\n");
00840                                 }
00841                                                                 found_at_pos++;
00842                                                                 upxfound=true;
00843                                                                 break;
00844                                                         }
00845                                                 }
00846                                         }
00847                                 }
00848                                 if (upxfound) break;
00849                         }
00850                         if (!upxfound) {
00851                                 LOG(LOG_BIOS,LOG_ERROR)("Codepage file %s invalid: %x",cp_filename,cpi_buf[0]);
00852                                 return KEYB_INVALIDCPFILE;
00853                         } else {
00854                                 if (next_byte<10) E_Exit("UPX-compressed cpi file, but upx-version too old");
00855 
00856                                 // read in compressed CPX-file
00857                                 fseek(tempfile, 0, SEEK_SET);
00858                                 size_of_cpxdata=(Bit32u)fread(cpi_buf, sizeof(Bit8u), 65536, tempfile);
00859                         }
00860                 } else {
00861                         // standard uncompressed cpi-file
00862                         fseek(tempfile, 0, SEEK_SET);
00863                         cpi_buf_size=(Bit32u)fread(cpi_buf, sizeof(Bit8u), 65536, tempfile);
00864                 }
00865         }
00866 
00867         if (upxfound) {
00868                 if (size_of_cpxdata>0xfe00) E_Exit("Size of cpx-compressed data too big");
00869 
00870                 found_at_pos+=19;
00871                 // prepare for direct decompression
00872                 cpi_buf[found_at_pos]=0xcb;
00873 
00874                 Bit16u seg=0;
00875                 Bit16u size=0x1500;
00876                 if (!DOS_AllocateMemory(&seg,&size)) E_Exit("Not enough free low memory to unpack data");
00877                 MEM_BlockWrite(((unsigned int)seg<<4u)+0x100u,cpi_buf,size_of_cpxdata);
00878 
00879                 // setup segments
00880                 Bit16u save_ds=SegValue(ds);
00881                 Bit16u save_es=SegValue(es);
00882                 Bit16u save_ss=SegValue(ss);
00883                 Bit32u save_esp=reg_esp;
00884                 SegSet16(ds,seg);
00885                 SegSet16(es,seg);
00886                 SegSet16(ss,seg+0x1000);
00887                 reg_esp=0xfffe;
00888 
00889                 // let UPX unpack the file
00890                 CALLBACK_RunRealFar(seg,0x100);
00891 
00892                 SegSet16(ds,save_ds);
00893                 SegSet16(es,save_es);
00894                 SegSet16(ss,save_ss);
00895                 reg_esp=save_esp;
00896 
00897                 // get unpacked content
00898                 MEM_BlockRead(((unsigned int)seg<<4u)+0x100u,cpi_buf,65536u);
00899                 cpi_buf_size=65536;
00900 
00901                 DOS_FreeMemory(seg);
00902         }
00903 
00904 
00905         start_pos=host_readd(&cpi_buf[0x13]);
00906         number_of_codepages=host_readw(&cpi_buf[start_pos]);
00907         start_pos+=4;
00908 
00909         // search if codepage is provided by file
00910         for (Bit16u test_codepage=0; test_codepage<number_of_codepages; test_codepage++) {
00911                 Bit16u device_type, font_codepage, font_type;
00912 
00913                 // device type can be display/printer (only the first is supported)
00914                 device_type=host_readw(&cpi_buf[start_pos+0x04]);
00915                 font_codepage=host_readw(&cpi_buf[start_pos+0x0e]);
00916 
00917                 Bit32u font_data_header_pt;
00918                 font_data_header_pt=host_readd(&cpi_buf[start_pos+0x16]);
00919 
00920                 font_type=host_readw(&cpi_buf[font_data_header_pt]);
00921 
00922                 if ((device_type==0x0001) && (font_type==0x0001) && (font_codepage==codepage_id)) {
00923                         // valid/matching codepage found
00924 
00925                         Bit16u number_of_fonts;//,font_data_length;
00926                         number_of_fonts=host_readw(&cpi_buf[font_data_header_pt+0x02]);
00927 //                      font_data_length=host_readw(&cpi_buf[font_data_header_pt+0x04]);
00928 
00929                         bool font_changed=false;
00930                         Bit32u font_data_start=font_data_header_pt+0x06;
00931 
00932                         // load all fonts if possible
00933                         for (Bit16u current_font=0; current_font<number_of_fonts; current_font++) {
00934                                 Bit8u font_height=cpi_buf[font_data_start];
00935                                 font_data_start+=6;
00936                                 if (font_height==0x10) {
00937                                         // 16x8 font, IF supported by the video card
00938                     if (int10.rom.font_16 != 0) {
00939                         PhysPt font16pt=Real2Phys(int10.rom.font_16);
00940                         for (Bit16u i=0;i<256*16;i++) {
00941                             phys_writeb(font16pt+i,cpi_buf[font_data_start+i]);
00942                         }
00943                         // terminate alternate list to prevent loading
00944                         phys_writeb(Real2Phys(int10.rom.font_16_alternate),0);
00945                         font_changed=true;
00946                     }
00947                                 } else if (font_height==0x0e) {
00948                                         // 14x8 font, IF supported by the video card
00949                     if (int10.rom.font_14 != 0) {
00950                         PhysPt font14pt=Real2Phys(int10.rom.font_14);
00951                         for (Bit16u i=0;i<256*14;i++) {
00952                             phys_writeb(font14pt+i,cpi_buf[font_data_start+i]);
00953                         }
00954                         // terminate alternate list to prevent loading
00955                         phys_writeb(Real2Phys(int10.rom.font_14_alternate),0);
00956                         font_changed=true;
00957                     }
00958                                 } else if (font_height==0x08) {
00959                     // 8x8 fonts. All video cards support it
00960                     if (int10.rom.font_8_first != 0) {
00961                         PhysPt font8pt=Real2Phys(int10.rom.font_8_first);
00962                         for (Bit16u i=0;i<128*8;i++) {
00963                             phys_writeb(font8pt+i,cpi_buf[font_data_start+i]);
00964                         }
00965                         font_changed=true;
00966                     }
00967                     if (int10.rom.font_8_second != 0) {
00968                         PhysPt font8pt=Real2Phys(int10.rom.font_8_second);
00969                         for (Bit16u i=0;i<128*8;i++) {
00970                             phys_writeb(font8pt+i,cpi_buf[font_data_start+i+128*8]);
00971                         }
00972                         font_changed=true;
00973                     }
00974                 }
00975                                 font_data_start+=font_height*256u;
00976                         }
00977 
00978                         LOG(LOG_BIOS,LOG_NORMAL)("Codepage %i successfully loaded",codepage_id);
00979 
00980                         // set codepage entries
00981                         dos.loaded_codepage=(Bit16u)(codepage_id&0xffff);
00982 
00983                         // update font if necessary (EGA/VGA/SVGA only)
00984                         if (font_changed && (CurMode->type==M_TEXT) && (IS_EGAVGA_ARCH)) {
00985                                 INT10_ReloadFont();
00986                         }
00987                         INT10_SetupRomMemoryChecksum();
00988 
00989                         return KEYB_NOERROR;
00990                 }
00991 
00992                 start_pos=host_readd(&cpi_buf[start_pos]);
00993                 start_pos+=2;
00994         }
00995 
00996         LOG(LOG_BIOS,LOG_ERROR)("Codepage %i not found",codepage_id);
00997 
00998         return KEYB_INVALIDCPFILE;
00999 }
01000 
01001 Bitu keyboard_layout::switch_keyboard_layout(const char* new_layout, keyboard_layout*& created_layout, Bit32s& tried_cp) {
01002         if (strncasecmp(new_layout,"US",2)) {
01003                 // switch to a foreign layout
01004                 char tbuf[256];
01005                 strcpy(tbuf, new_layout);
01006                 size_t newlen=strlen(tbuf);
01007 
01008                 bool language_code_found=false;
01009                 // check if language code is present in loaded foreign layout
01010                 for (Bitu i=0; i<language_code_count; i++) {
01011                         if (!strncasecmp(tbuf,language_codes[i],newlen)) {
01012                                 language_code_found=true;
01013                                 break;
01014                         }
01015                 }
01016 
01017                 if (language_code_found) {
01018                         if (!this->use_foreign_layout) {
01019                                 // switch to foreign layout
01020                                 this->use_foreign_layout=true;
01021                                 diacritics_character=0;
01022                                 LOG(LOG_BIOS,LOG_NORMAL)("Switched to layout %s",tbuf);
01023                         }
01024                 } else {
01025                         keyboard_layout * temp_layout=new keyboard_layout();
01026                         Bit16u req_codepage=temp_layout->extract_codepage(new_layout);
01027                         tried_cp = req_codepage;
01028                         Bitu kerrcode=temp_layout->read_keyboard_file(new_layout, req_codepage);
01029                         if (kerrcode) {
01030                                 delete temp_layout;
01031                                 return kerrcode;
01032                         }
01033                         // ...else keyboard layout loaded successfully, change codepage accordingly
01034                         kerrcode=temp_layout->read_codepage_file("auto", req_codepage);
01035                         if (kerrcode) {
01036                                 delete temp_layout;
01037                                 return kerrcode;
01038                         }
01039                         // Everything went fine, switch to new layout
01040                         created_layout=temp_layout;
01041                 }
01042         } else if (this->use_foreign_layout) {
01043                 // switch to the US layout
01044                 this->use_foreign_layout=false;
01045                 diacritics_character=0;
01046                 LOG(LOG_BIOS,LOG_NORMAL)("Switched to US layout");
01047         }
01048         return KEYB_NOERROR;
01049 }
01050 
01051 void keyboard_layout::switch_foreign_layout() {
01052         this->use_foreign_layout=!this->use_foreign_layout;
01053         diacritics_character=0;
01054         if (this->use_foreign_layout) LOG(LOG_BIOS,LOG_NORMAL)("Switched to foreign layout");
01055         else LOG(LOG_BIOS,LOG_NORMAL)("Switched to US layout");
01056 }
01057 
01058 const char* keyboard_layout::get_layout_name() {
01059         // get layout name (language ID or NULL if default layout)
01060         if (use_foreign_layout) {
01061                 if (strcmp(current_keyboard_file_name,"none") != 0) {
01062                         return (const char*)&current_keyboard_file_name;
01063                 }
01064         }
01065         return NULL;
01066 }
01067 
01068 const char* keyboard_layout::main_language_code() {
01069         if (language_codes) {
01070                 return language_codes[0];
01071         }
01072         return NULL;
01073 }
01074 
01075 
01076 static keyboard_layout* loaded_layout=NULL;
01077 
01078 // CTRL-ALT-F2 switches between foreign and US-layout using this function
01079 /* static void switch_keyboard_layout(bool pressed) {
01080         if (!pressed)
01081                 return;
01082         if (loaded_layout) loaded_layout->switch_foreign_layout();
01083 } */
01084 
01085 // called by int9-handler
01086 bool DOS_LayoutKey(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3) {
01087         if (loaded_layout) return loaded_layout->layout_key(key, flags1, flags2, flags3);
01088         else return false;
01089 }
01090 
01091 Bitu DOS_LoadKeyboardLayout(const char * layoutname, Bit32s codepage, const char * codepagefile) {
01092         keyboard_layout * temp_layout=new keyboard_layout();
01093         // try to read the layout for the specified codepage
01094         Bitu kerrcode=temp_layout->read_keyboard_file(layoutname, codepage);
01095         if (kerrcode) {
01096                 delete temp_layout;
01097                 return kerrcode;
01098         }
01099         // ...else keyboard layout loaded successfully, change codepage accordingly
01100         kerrcode=temp_layout->read_codepage_file(codepagefile, codepage);
01101         if (kerrcode) {
01102                 delete temp_layout;
01103                 return kerrcode;
01104         }
01105         // Everything went fine, switch to new layout
01106         loaded_layout=temp_layout;
01107         return KEYB_NOERROR;
01108 }
01109 
01110 Bitu DOS_SwitchKeyboardLayout(const char* new_layout, Bit32s& tried_cp) {
01111         if (loaded_layout) {
01112                 keyboard_layout* changed_layout=NULL;
01113                 Bitu ret_code=loaded_layout->switch_keyboard_layout(new_layout, changed_layout, tried_cp);
01114                 if (changed_layout) {
01115                         // Remove old layout, activate new layout
01116                         delete loaded_layout;
01117                         loaded_layout=changed_layout;
01118                 }
01119                 return ret_code;
01120         } else return 0xff;
01121 }
01122 
01123 // get currently loaded layout name (NULL if no layout is loaded)
01124 const char* DOS_GetLoadedLayout(void) {
01125         if (loaded_layout) {
01126                 return loaded_layout->get_layout_name();
01127         }
01128         return NULL;
01129 }
01130 
01131 
01132 class DOS_KeyboardLayout: public Module_base {
01133 public:
01134         DOS_KeyboardLayout(Section* configuration):Module_base(configuration){
01135         const Section_prop* section = static_cast<Section_prop*>(configuration);
01136                 dos.loaded_codepage=(IS_PC98_ARCH ? 932 : 437); // US codepage already initialized
01137                 loaded_layout=new keyboard_layout();
01138 
01139                 const char * layoutname=section->Get_string("keyboardlayout");
01140 
01141                 Bits wants_dos_codepage = -1;
01142                 if (!strncmp(layoutname,"auto",4)) {
01143 #if defined (WIN32)
01144                         WORD cur_kb_layout = LOWORD(GetKeyboardLayout(0));
01145                         WORD cur_kb_subID  = 0;
01146                         char layoutID_string[KL_NAMELENGTH];
01147                         if (GetKeyboardLayoutName(layoutID_string)) {
01148                                 if (strlen(layoutID_string) == 8) {
01149                                         int cur_kb_layout_by_name = (int)ConvHexWord((char*)&layoutID_string[4]);
01150                                         layoutID_string[4] = 0;
01151                                         int subID = (int)ConvHexWord((char*)&layoutID_string[0]);
01152                                         if ((cur_kb_layout_by_name>0) && (cur_kb_layout_by_name<65536)) {
01153                                                 // use layout ID extracted from the layout string
01154                                                 cur_kb_layout = (WORD)cur_kb_layout_by_name;
01155                                         }
01156                                         if ((subID>=0) && (subID<100)) {
01157                                                 // use sublanguage ID extracted from the layout string
01158                                                 cur_kb_subID  = (WORD)subID;
01159                                         }
01160                                 }
01161                         }
01162                         // try to match emulated keyboard layout with host-keyboardlayout
01163                         // codepage 437 (standard) is preferred
01164                         switch (cur_kb_layout) {
01165 /*                              case 1026:
01166                                         layoutname = "bg241";
01167                                         break; */
01168                                 case 1029:
01169                                         layoutname = "cz243";
01170                                         break;
01171                                 case 1030:
01172                                         layoutname = "dk";
01173                                         break;
01174                                 case 1031:
01175                                         layoutname = "gr";
01176                                         wants_dos_codepage = (IS_PC98_ARCH ? 932 : 437);
01177                                         break;
01178                                 case 1033:
01179                                         // US
01180                                         return;
01181                                 case 1032:
01182                                         layoutname = "gk";
01183                                         break;
01184                                 case 1034:
01185                                         layoutname = "sp";
01186                                         wants_dos_codepage = (IS_PC98_ARCH ? 932 : 437);
01187                                         break;
01188                                 case 1035:
01189                                         layoutname = "su";
01190                                         wants_dos_codepage = (IS_PC98_ARCH ? 932 : 437);
01191                                         break;
01192                                 case 1036:
01193                                         layoutname = "fr";
01194                                         wants_dos_codepage = (IS_PC98_ARCH ? 932 : 437);
01195                                         break;
01196                                 case 1038:
01197                                         if (cur_kb_subID==1) layoutname = "hu";
01198                                         else layoutname = "hu208";
01199                                         break;
01200                                 case 1039:
01201                                         layoutname = "is161";
01202                                         break;
01203                                 case 1040:
01204                                         layoutname = "it";
01205                                         wants_dos_codepage = (IS_PC98_ARCH ? 932 : 437);
01206                                         break;
01207                                 case 1043:
01208                                         layoutname = "nl";
01209                                         wants_dos_codepage = (IS_PC98_ARCH ? 932 : 437);
01210                                         break;
01211                                 case 1044:
01212                                         layoutname = "no";
01213                                         break;
01214                                 case 1045:
01215                                         layoutname = "pl";
01216                                         break;
01217                                 case 1046:
01218                                         layoutname = "br";
01219                                         wants_dos_codepage = (IS_PC98_ARCH ? 932 : 437);
01220                                         break;
01221 /*                              case 1048:
01222                                         layoutname = "ro446";
01223                                         break; */
01224                                 case 1049:
01225                                         layoutname = "ru";
01226                                         wants_dos_codepage = (IS_PC98_ARCH ? 932 : 437);
01227                                         break;
01228                                 case 1050:
01229                                         layoutname = "hr";
01230                                         break;
01231                                 case 1051:
01232                                         layoutname = "sk";
01233                                         break;
01234 /*                              case 1052:
01235                                         layoutname = "sq448";
01236                                         break; */
01237                                 case 1053:
01238                                         layoutname = "sv";
01239                                         wants_dos_codepage = (IS_PC98_ARCH ? 932 : 437);
01240                                         break;
01241                                 case 1055:
01242                                         layoutname = "tr";
01243                                         break;
01244                                 case 1058:
01245                                         layoutname = "ur";
01246                                         wants_dos_codepage = (IS_PC98_ARCH ? 932 : 437);
01247                                         break;
01248                                 case 1059:
01249                                         layoutname = "bl";
01250                                         break;
01251                                 case 1060:
01252                                         layoutname = "si";
01253                                         break;
01254                                 case 1061:
01255                                         layoutname = "et";
01256                                         break;
01257 /*                              case 1062:
01258                                         layoutname = "lv";
01259                                         break; */
01260 /*                              case 1063:
01261                                         layoutname = "lt221";
01262                                         break; */
01263 /*                              case 1064:
01264                                         layoutname = "tj";
01265                                         break;
01266                                 case 1066:
01267                                         layoutname = "vi";
01268                                         break;
01269                                 case 1067:
01270                                         layoutname = "hy";
01271                                         break; */
01272                                 case 2055:
01273                                         layoutname = "sg";
01274                                         wants_dos_codepage = (IS_PC98_ARCH ? 932 : 437);
01275                                         break;
01276                                 case 2070:
01277                                         layoutname = "po";
01278                                         break;
01279                                 case 4108:
01280                                         layoutname = "sf";
01281                                         wants_dos_codepage = (IS_PC98_ARCH ? 932 : 437);
01282                                         break;
01283                                 case 1041:
01284                                         layoutname = "jp";
01285                                         break;
01286                                 default:
01287                                         break;
01288                         }
01289 #endif
01290                 }
01291 
01292                 bool extract_codepage = true;
01293                 if (wants_dos_codepage>0) {
01294                         if ((loaded_layout->read_codepage_file("auto", (Bit32s)wants_dos_codepage)) == KEYB_NOERROR) {
01295                                 // preselected codepage was successfully loaded
01296                                 extract_codepage = false;
01297                         }
01298                 }
01299                 if (extract_codepage) {
01300                         // try to find a good codepage for the requested layout
01301                         Bit16u req_codepage = loaded_layout->extract_codepage(layoutname);
01302                         loaded_layout->read_codepage_file("auto", req_codepage);
01303                 }
01304 
01305 /*              if (strncmp(layoutname,"auto",4) && strncmp(layoutname,"none",4)) {
01306                         LOG_MSG("Loading DOS keyboard layout %s ...",layoutname);
01307                 } */
01308                 if (loaded_layout->read_keyboard_file(layoutname, dos.loaded_codepage)) {
01309                         if (strncmp(layoutname,"auto",4)) {
01310                                 LOG_MSG("Error loading keyboard layout %s",layoutname);
01311                         }
01312                 } else {
01313                         const char* lcode = loaded_layout->main_language_code();
01314                         if (lcode) {
01315                                 LOG_MSG("DOS keyboard layout loaded with main language code %s for layout %s",lcode,layoutname);
01316                         }
01317                 }
01318         }
01319 
01320         ~DOS_KeyboardLayout(){
01321                 if ((dos.loaded_codepage!=(IS_PC98_ARCH ? 932 : 437)) && (CurMode->type==M_TEXT)) {
01322                         INT10_ReloadRomFonts();
01323                         dos.loaded_codepage=(IS_PC98_ARCH ? 932 : 437); // US codepage
01324                 }
01325                 if (loaded_layout) {
01326                         delete loaded_layout;
01327                         loaded_layout=NULL;
01328                 }
01329         }
01330 };
01331 
01332 static DOS_KeyboardLayout* test;
01333 
01334 void DOS_KeyboardLayout_ShutDown(Section* /*sec*/) {
01335         if (test != NULL) {
01336                 delete test;
01337                 test = NULL;
01338         }
01339 }
01340 
01341 void DOS_KeyboardLayout_Startup(Section* sec) {
01342     (void)sec;//UNUSED
01343         if (test == NULL) {
01344                 LOG(LOG_MISC,LOG_DEBUG)("Reinitializing DOS keyboard layout support");
01345                 test = new DOS_KeyboardLayout(control->GetSection("dos"));
01346         }
01347 }
01348 
01349 void DOS_KeyboardLayout_Init() {
01350         LOG(LOG_MISC,LOG_DEBUG)("Initializing DOS keyboard layout emulation");
01351 
01352         AddExitFunction(AddExitFunctionFuncPair(DOS_KeyboardLayout_ShutDown),true);
01353         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(DOS_KeyboardLayout_ShutDown));
01354         AddVMEventFunction(VM_EVENT_DOS_EXIT_BEGIN,AddVMEventFunctionFuncPair(DOS_KeyboardLayout_ShutDown));
01355 }
01356