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