DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dos_keyboard_layout.cpp
00001 /*
00002  *  Copyright (C) 2002-2019  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, 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             size_t readResult = fread(rbuf, sizeof(Bit8u), 2, tempfile);
00180             if (readResult != 2) {
00181                 LOG(LOG_IO, LOG_ERROR) ("Reading error in read_kcl_file\n");
00182                 return 0;
00183             }
00184                         Bit16u lcnum=host_readw(&rbuf[0]);
00185                         i+=2;
00186                         Bitu lcpos=0;
00187                         for (;i<data_len;) {
00188                 readResult = fread(rbuf, sizeof(Bit8u), 1, tempfile);
00189                 if (readResult != 1) {
00190                     LOG(LOG_IO, LOG_ERROR) ("Reading error in read_kcl_file\n");
00191                     return 0;
00192                 }
00193                                 i++;
00194                                 if (((char)rbuf[0])==',') break;
00195                                 lng_codes[lcpos++]=(char)rbuf[0];
00196                         }
00197                         lng_codes[lcpos]=0;
00198                         if (strcasecmp(lng_codes, layout_id)==0) {
00199                                 // language ID found in file, return file position
00200                                 fclose(tempfile);
00201                                 return cur_pos;
00202                         }
00203                         if (first_id_only) break;
00204                         if (lcnum) {
00205                                 sprintf(&lng_codes[lcpos],"%d",lcnum);
00206                                 if (strcasecmp(lng_codes, layout_id)==0) {
00207                                         // language ID found in file, return file position
00208                                         return cur_pos;
00209                                 }
00210                         }
00211                 }
00212                 fseek(tempfile, long(cur_pos+3+len), SEEK_SET);
00213         }
00214 
00215         fclose(tempfile);
00216         return 0;
00217 }
00218 
00219 static Bit32u read_kcl_data(Bit8u * kcl_data, Bit32u kcl_data_size, const char* layout_id, bool first_id_only) {
00220         // check ID-bytes
00221         if ((kcl_data[0]!=0x4b) || (kcl_data[1]!=0x43) || (kcl_data[2]!=0x46)) {
00222                 return 0;
00223         }
00224 
00225         Bit32u dpos=7u+kcl_data[6];
00226 
00227         for (;;) {
00228                 if (dpos+5>kcl_data_size) break;
00229                 Bit32u cur_pos=dpos;
00230                 Bit16u len=host_readw(&kcl_data[dpos]);
00231                 Bit8u data_len=kcl_data[dpos+2];
00232                 dpos+=5;
00233 
00234                 char lng_codes[258];
00235                 // get all language codes for this layout
00236                 for (Bitu i=0; i<data_len;) {
00237                         Bit16u lcnum=host_readw(&kcl_data[dpos-2]);
00238                         i+=2;
00239                         Bitu lcpos=0;
00240                         for (;i<data_len;) {
00241                                 if (dpos+1>kcl_data_size) break;
00242                                 char lc=(char)kcl_data[dpos];
00243                                 dpos++;
00244                                 i++;
00245                                 if (lc==',') break;
00246                                 lng_codes[lcpos++]=lc;
00247                         }
00248                         lng_codes[lcpos]=0;
00249                         if (strcasecmp(lng_codes, layout_id)==0) {
00250                                 // language ID found, return position
00251                                 return cur_pos;
00252                         }
00253                         if (first_id_only) break;
00254                         if (lcnum) {
00255                                 sprintf(&lng_codes[lcpos],"%d",lcnum);
00256                                 if (strcasecmp(lng_codes, layout_id)==0) {
00257                                         // language ID found, return position
00258                                         return cur_pos;
00259                                 }
00260                         }
00261                         dpos+=2;
00262                 }
00263                 dpos=cur_pos+3+len;
00264         }
00265         return 0;
00266 }
00267 
00268 Bitu keyboard_layout::read_keyboard_file(const char* keyboard_file_name, Bit32s specific_layout, Bit32s requested_codepage) {
00269         this->reset();
00270 
00271         if (specific_layout==-1) strcpy(current_keyboard_file_name, keyboard_file_name);
00272         if (!strcmp(keyboard_file_name,"none")) return KEYB_NOERROR;
00273 
00274         static Bit8u read_buf[65535];
00275         Bit32u read_buf_size, read_buf_pos, bytes_read;
00276         Bit32u start_pos=5;
00277 
00278         char nbuf[512];
00279         read_buf_size = 0;
00280         sprintf(nbuf, "%s.kl", keyboard_file_name);
00281         FILE* tempfile = OpenDosboxFile(nbuf);
00282         if (tempfile==NULL) {
00283                 // try keyboard layout libraries next
00284                 if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,true))) {
00285                         tempfile = OpenDosboxFile("keyboard.sys");
00286                 } else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,true))) {
00287                         tempfile = OpenDosboxFile("keybrd2.sys");
00288                 } else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,true))) {
00289                         tempfile = OpenDosboxFile("keybrd3.sys");
00290                 } else if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,false))) {
00291                         tempfile = OpenDosboxFile("keyboard.sys");
00292                 } else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,false))) {
00293                         tempfile = OpenDosboxFile("keybrd2.sys");
00294                 } else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,false))) {
00295                         tempfile = OpenDosboxFile("keybrd3.sys");
00296                 } else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,true))) {
00297                         read_buf_size=0;
00298                         for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
00299                 } else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,true))) {
00300                         read_buf_size=0;
00301                         for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
00302                 } else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,true))) {
00303                         read_buf_size=0;
00304                         for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
00305                 } else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,false))) {
00306                         read_buf_size=0;
00307                         for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
00308                 } else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,false))) {
00309                         read_buf_size=0;
00310                         for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
00311                 } else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,false))) {
00312                         read_buf_size=0;
00313                         for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
00314                 } else {
00315                         LOG(LOG_BIOS,LOG_ERROR)("Keyboard layout file %s not found",keyboard_file_name);
00316                         return KEYB_FILENOTFOUND;
00317                 }
00318                 if (tempfile) {
00319                         fseek(tempfile, long(start_pos+2), SEEK_SET);
00320                         read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
00321                         fclose(tempfile);
00322                 }
00323                 start_pos=0;
00324         } else {
00325                 // check ID-bytes of file
00326                 Bit32u dr=(Bit32u)fread(read_buf, sizeof(Bit8u), 4, tempfile);
00327                 if ((dr<4) || (read_buf[0]!=0x4b) || (read_buf[1]!=0x4c) || (read_buf[2]!=0x46)) {
00328                         LOG(LOG_BIOS,LOG_ERROR)("Invalid keyboard layout file %s",keyboard_file_name);
00329                         return KEYB_INVALIDFILE;
00330                 }
00331                 
00332                 fseek(tempfile, 0, SEEK_SET);
00333                 read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
00334                 fclose(tempfile);
00335         }
00336 
00337         Bit8u data_len,submappings;
00338         data_len=read_buf[start_pos++];
00339 
00340         language_codes=new char*[data_len];
00341         language_code_count=0;
00342         // get all language codes for this layout
00343         for (Bitu i=0; i<data_len;) {
00344                 language_codes[language_code_count]=new char[256];
00345                 i+=2;
00346                 Bitu lcpos=0;
00347                 for (;i<data_len;) {
00348                         char lcode=char(read_buf[start_pos+i]);
00349                         i++;
00350                         if (lcode==',') break;
00351                         language_codes[language_code_count][lcpos++]=lcode;
00352                 }
00353                 language_codes[language_code_count][lcpos]=0;
00354                 language_code_count++;
00355         }
00356 
00357         start_pos+=data_len;            // start_pos==absolute position of KeybCB block
00358 
00359         submappings=read_buf[start_pos];
00360         additional_planes=read_buf[start_pos+1];
00361 
00362         // four pages always occupied by normal,shift,flags,commandbits
00363         if (additional_planes>(layout_pages-4)) additional_planes=(layout_pages-4);
00364 
00365         // seek to plane descriptor
00366         read_buf_pos=start_pos+0x14u+submappings*8u;
00367         for (Bit16u cplane=0; cplane<additional_planes; cplane++) {
00368                 Bit16u plane_flags;
00369 
00370                 // get required-flags (shift/alt/ctrl-states etc.)
00371                 plane_flags=host_readw(&read_buf[read_buf_pos]);
00372                 read_buf_pos+=2;
00373                 current_layout_planes[cplane].required_flags=plane_flags;
00374                 used_lock_modifiers |= (plane_flags&0x70);
00375                 // get forbidden-flags
00376                 plane_flags=host_readw(&read_buf[read_buf_pos]);
00377                 read_buf_pos+=2;
00378                 current_layout_planes[cplane].forbidden_flags=plane_flags;
00379 
00380                 // get required-userflags
00381                 plane_flags=host_readw(&read_buf[read_buf_pos]);
00382                 read_buf_pos+=2;
00383                 current_layout_planes[cplane].required_userflags=plane_flags;
00384                 // get forbidden-userflags
00385                 plane_flags=host_readw(&read_buf[read_buf_pos]);
00386                 read_buf_pos+=2;
00387                 current_layout_planes[cplane].forbidden_userflags=plane_flags;
00388         }
00389 
00390         bool found_matching_layout=false;
00391         
00392         // check all submappings and use them if general submapping or same codepage submapping
00393         for (Bit16u sub_map=0; (sub_map<submappings) && (!found_matching_layout); sub_map++) {
00394                 Bit16u submap_cp, table_offset;
00395 
00396                 if ((sub_map!=0) && (specific_layout!=-1)) sub_map=(Bit16u)(specific_layout&0xffff);
00397 
00398                 // read codepage of submapping
00399                 submap_cp=host_readw(&read_buf[start_pos+0x14u+sub_map*8u]);
00400                 if ((submap_cp!=0) && (submap_cp!=requested_codepage) && (specific_layout==-1))
00401                         continue;               // skip nonfitting submappings
00402 
00403                 if (submap_cp==requested_codepage) found_matching_layout=true;
00404 
00405                 // get table offset
00406                 table_offset=host_readw(&read_buf[start_pos+0x18u+sub_map*8u]);
00407                 diacritics_entries=0;
00408                 if (table_offset!=0) {
00409                         // process table
00410                         Bit16u i,j;
00411                         for (i=0; i<2048;) {
00412                                 if (read_buf[start_pos+table_offset+i]==0) break;       // end of table
00413                                 diacritics_entries++;
00414                                 i+=read_buf[start_pos+table_offset+i+1]*2+2;
00415                         }
00416                         // copy diacritics table
00417                         for (j=0; j<=i; j++) diacritics[j]=read_buf[start_pos+table_offset+j];
00418                 }
00419 
00420 
00421                 // get table offset
00422                 table_offset=host_readw(&read_buf[start_pos+0x16u+sub_map*8u]);
00423                 if (table_offset==0) continue;  // non-present table
00424 
00425                 read_buf_pos=start_pos+table_offset;
00426 
00427                 bytes_read=read_buf_size-read_buf_pos;
00428 
00429                 // process submapping table
00430                 for (Bit32u i=0; i<bytes_read;) {
00431                         Bit8u scan=read_buf[read_buf_pos++];
00432                         if (scan==0) break;
00433                         Bit8u scan_length=(read_buf[read_buf_pos]&7)+1;         // length of data struct
00434                         read_buf_pos+=2;
00435                         i+=3;
00436                         if (((scan&0x7f)<=MAX_SCAN_CODE) && (scan_length>0)) {
00437                                 // add all available mappings
00438                                 for (Bit16u addmap=0; addmap<scan_length; addmap++) {
00439                                         if (addmap>additional_planes+2) break;
00440                                         Bitu charptr=read_buf_pos+addmap*((read_buf[read_buf_pos-2u]&0x80u)?2u:1u);
00441                                         Bit16u kchar=read_buf[charptr];
00442 
00443                                         if (kchar!=0) {         // key remapped
00444                                                 if (read_buf[read_buf_pos-2]&0x80) kchar|=read_buf[charptr+1]<<8;       // scancode/char pair
00445                                                 // overwrite mapping
00446                                                 current_layout[scan*layout_pages+addmap]=kchar;
00447                                                 // clear command bit
00448                                                 current_layout[scan*layout_pages+layout_pages-2]&=~(1<<addmap);
00449                                                 // add command bit
00450                                                 current_layout[scan*layout_pages+layout_pages-2]|=(read_buf[read_buf_pos-1] & (1<<addmap));
00451                                         }
00452                                 }
00453 
00454                                 // calculate max length of entries, taking into account old number of entries
00455                                 Bit8u new_flags=current_layout[scan*layout_pages+layout_pages-1]&0x7;
00456                                 if ((read_buf[read_buf_pos-2]&0x7) > new_flags) new_flags = read_buf[read_buf_pos-2]&0x7;
00457 
00458                                 // merge flag bits in as well
00459                                 new_flags |= (read_buf[read_buf_pos-2] | current_layout[scan*layout_pages+layout_pages-1]) & 0xf0;
00460 
00461                                 current_layout[scan*layout_pages+layout_pages-1]=new_flags;
00462                                 if (read_buf[read_buf_pos-2]&0x80) scan_length*=2;              // granularity flag (S)
00463                         }
00464                         i+=scan_length;         // advance pointer
00465                         read_buf_pos+=scan_length;
00466                 }
00467                 if (specific_layout==sub_map) break;
00468         }
00469 
00470         if (found_matching_layout) {
00471                 if (specific_layout==-1) LOG(LOG_BIOS,LOG_NORMAL)("Keyboard layout %s successfully loaded",keyboard_file_name);
00472                 else LOG(LOG_BIOS,LOG_NORMAL)("Keyboard layout %s (%i) successfully loaded",keyboard_file_name,specific_layout);
00473                 this->use_foreign_layout=true;
00474                 return KEYB_NOERROR;
00475         }
00476 
00477         LOG(LOG_BIOS,LOG_ERROR)("No matching keyboard layout found in %s",keyboard_file_name);
00478 
00479         // reset layout data (might have been changed by general layout)
00480         this->reset();
00481 
00482         return KEYB_LAYOUTNOTFOUND;
00483 }
00484 
00485 bool keyboard_layout::layout_key(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3) {
00486         if (key>MAX_SCAN_CODE) return false;
00487         if (!this->use_foreign_layout) return false;
00488 
00489         bool is_special_pair=(current_layout[key*layout_pages+layout_pages-1] & 0x80)==0x80;
00490 
00491         if ((((flags1&used_lock_modifiers)&0x7c)==0) && ((flags3&2)==0)) {
00492                 // check if shift/caps is active:
00493                 // (left_shift OR right_shift) XOR (key_affected_by_caps AND caps_locked)
00494                 if ((((flags1&2)>>1) | (flags1&1)) ^ (((current_layout[key*layout_pages+layout_pages-1] & 0x40) & (flags1 & 0x40))>>6)) {
00495                         // shift plane
00496                         if (current_layout[key*layout_pages+1]!=0) {
00497                                 // check if command-bit is set for shift plane
00498                                 bool is_command=(current_layout[key*layout_pages+layout_pages-2]&2)!=0;
00499                                 if (this->map_key(key, current_layout[key*layout_pages+1],
00500                                         is_command, is_special_pair)) return true;
00501                         }
00502                 } else {
00503                         // normal plane
00504                         if (current_layout[key*layout_pages]!=0) {
00505                                 // check if command-bit is set for normal plane
00506                                 bool is_command=(current_layout[key*layout_pages+layout_pages-2]&1)!=0;
00507                                 if (this->map_key(key, current_layout[key*layout_pages],
00508                                         is_command, is_special_pair)) return true;
00509                         }
00510                 }
00511         }
00512 
00513         // calculate current flags
00514         Bit16u current_flags=(flags1&0x7f) | (((flags2&3) | (flags3&0xc))<<8);
00515         if (flags1&3) current_flags|=0x4000;    // either shift key active
00516         if (flags3&2) current_flags|=0x1000;    // e0 prefixed
00517 
00518         // check all planes if flags fit
00519         for (Bit16u cplane=0; cplane<additional_planes; cplane++) {
00520                 Bit16u req_flags=current_layout_planes[cplane].required_flags;
00521                 Bit16u req_userflags=current_layout_planes[cplane].required_userflags;
00522                 // test flags
00523                 if (((current_flags & req_flags)==req_flags) &&
00524                         ((user_keys & req_userflags)==req_userflags) &&
00525                         ((current_flags & current_layout_planes[cplane].forbidden_flags)==0) &&
00526                         ((user_keys & current_layout_planes[cplane].forbidden_userflags)==0)) {
00527                                 // remap key
00528                                 if (current_layout[key*layout_pages+2+cplane]!=0) {
00529                                         // check if command-bit is set for this plane
00530                                         bool is_command=((current_layout[key*layout_pages+layout_pages-2]>>(cplane+2))&1)!=0;
00531                                         if (this->map_key(key, current_layout[key*layout_pages+2+cplane],
00532                                                 is_command, is_special_pair)) return true;
00533                                 } else break;   // abort plane checking
00534                         }
00535         }
00536 
00537         if (diacritics_character>0) {
00538                 // ignore state-changing keys
00539                 switch(key) {
00540                         case 0x1d:                      /* Ctrl Pressed */
00541                         case 0x2a:                      /* Left Shift Pressed */
00542                         case 0x36:                      /* Right Shift Pressed */
00543                         case 0x38:                      /* Alt Pressed */
00544                         case 0x3a:                      /* Caps Lock */
00545                         case 0x45:                      /* Num Lock */
00546                         case 0x46:                      /* Scroll Lock */
00547                                 break;
00548                         default:
00549                                 if (diacritics_character-200>=diacritics_entries) {
00550                                         diacritics_character=0;
00551                                         return true;
00552                                 }
00553                                 Bit16u diacritics_start=0;
00554                                 // search start of subtable
00555                                 for (Bit16u i=0; i<diacritics_character-200; i++)
00556                                         diacritics_start+=diacritics[diacritics_start+1]*2+2;
00557 
00558                                 BIOS_AddKeyToBuffer((Bit16u)(key<<8) | diacritics[diacritics_start]);
00559                                 diacritics_character=0;
00560                 }
00561         }
00562 
00563         return false;
00564 }
00565 
00566 bool keyboard_layout::map_key(Bitu key, Bit16u layouted_key, bool is_command, bool is_keypair) {
00567         if (is_command) {
00568                 Bit8u key_command=(Bit8u)(layouted_key&0xff);
00569                 // check if diacritics-command
00570                 if ((key_command>=200) && (key_command<235)) {
00571                         // diacritics command
00572                         diacritics_character=key_command;
00573                         if (diacritics_character-200>=diacritics_entries) diacritics_character=0;
00574                         return true;
00575                 } else if ((key_command>=120) && (key_command<140)) {
00576                         // switch layout command
00577                         this->read_keyboard_file(key_command-119);
00578                         return true;
00579                 } else if ((key_command>=180) && (key_command<188)) {
00580                         // switch user key off
00581                         user_keys&=~(1<<(key_command-180));
00582                         return true;
00583                 } else if ((key_command>=188) && (key_command<196)) {
00584                         // switch user key on
00585                         user_keys|=(1<<(key_command-188));
00586                         return true;
00587                 } else if (key_command==160) return true;       // nop command
00588         } else {
00589                 // non-command
00590                 if (diacritics_character>0) {
00591                         if (diacritics_character-200>=diacritics_entries) diacritics_character = 0;
00592                         else {
00593                                 Bit16u diacritics_start=0;
00594                                 // search start of subtable
00595                                 for (Bit16u i=0; i<diacritics_character-200; i++)
00596                                         diacritics_start+=diacritics[diacritics_start+1]*2+2;
00597 
00598                                 Bit8u diacritics_length=diacritics[diacritics_start+1];
00599                                 diacritics_start+=2;
00600                                 diacritics_character=0; // reset
00601 
00602                                 // search scancode
00603                                 for (Bit16u i=0; i<diacritics_length; i++) {
00604                                         if (diacritics[diacritics_start+i*2]==(layouted_key&0xff)) {
00605                                                 // add diacritics to keybuf
00606                                                 BIOS_AddKeyToBuffer((Bit16u)(key<<8) | diacritics[diacritics_start+i*2+1]);
00607                                                 return true;
00608                                         }
00609                                 }
00610                                 // add standard-diacritics to keybuf
00611                                 BIOS_AddKeyToBuffer((Bit16u)(key<<8) | diacritics[diacritics_start-2]);
00612                         }
00613                 }
00614 
00615                 // add remapped key to keybuf
00616                 if (is_keypair) BIOS_AddKeyToBuffer(layouted_key);
00617                 else BIOS_AddKeyToBuffer((Bit16u)(key<<8) | (layouted_key&0xff));
00618 
00619                 return true;
00620         }
00621         return false;
00622 }
00623 
00624 Bit16u keyboard_layout::extract_codepage(const char* keyboard_file_name) {
00625         if (!strcmp(keyboard_file_name,"none")) return (IS_PC98_ARCH ? 932 : 437);
00626 
00627         Bit32u read_buf_size;
00628         static Bit8u read_buf[65535];
00629         Bit32u start_pos=5;
00630 
00631         char nbuf[512];
00632         sprintf(nbuf, "%s.kl", keyboard_file_name);
00633         FILE* tempfile = OpenDosboxFile(nbuf);
00634         if (tempfile==NULL) {
00635                 // try keyboard layout libraries next
00636                 if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,true))) {
00637                         tempfile = OpenDosboxFile("keyboard.sys");
00638                 } else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,true))) {
00639                         tempfile = OpenDosboxFile("keybrd2.sys");
00640                 } else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,true))) {
00641                         tempfile = OpenDosboxFile("keybrd3.sys");
00642                 } else if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,false))) {
00643                         tempfile = OpenDosboxFile("keyboard.sys");
00644                 } else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,false))) {
00645                         tempfile = OpenDosboxFile("keybrd2.sys");
00646                 } else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,false))) {
00647                         tempfile = OpenDosboxFile("keybrd3.sys");
00648                 } else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,true))) {
00649                         read_buf_size=0;
00650                         for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
00651                 } else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,true))) {
00652                         read_buf_size=0;
00653                         for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
00654                 } else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,true))) {
00655                         read_buf_size=0;
00656                         for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
00657                 } else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,false))) {
00658                         read_buf_size=0;
00659                         for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
00660                 } else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,false))) {
00661                         read_buf_size=0;
00662                         for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
00663                 } else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,false))) {
00664                         read_buf_size=0;
00665                         for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
00666                 } else {
00667                         start_pos=0;
00668                         LOG(LOG_BIOS,LOG_ERROR)("Keyboard layout file %s not found",keyboard_file_name);
00669                         return (IS_PC98_ARCH ? 932 : 437);
00670                 }
00671                 if (tempfile) {
00672                         fseek(tempfile, long(start_pos+2), SEEK_SET);
00673                         read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
00674                         fclose(tempfile);
00675                 }
00676                 start_pos=0;
00677         } else {
00678                 // check ID-bytes of file
00679                 Bit32u dr=(Bit32u)fread(read_buf, sizeof(Bit8u), 4, tempfile);
00680                 if ((dr<4) || (read_buf[0]!=0x4b) || (read_buf[1]!=0x4c) || (read_buf[2]!=0x46)) {
00681                         LOG(LOG_BIOS,LOG_ERROR)("Invalid keyboard layout file %s",keyboard_file_name);
00682                         return (IS_PC98_ARCH ? 932 : 437);
00683                 }
00684 
00685                 fseek(tempfile, 0, SEEK_SET);
00686                 read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
00687                 fclose(tempfile);
00688         }
00689 
00690         Bit8u data_len,submappings;
00691         data_len=read_buf[start_pos++];
00692 
00693         start_pos+=data_len;            // start_pos==absolute position of KeybCB block
00694 
00695         submappings=read_buf[start_pos];
00696 
00697         // check all submappings and use them if general submapping or same codepage submapping
00698         for (Bit16u sub_map=0; (sub_map<submappings); sub_map++) {
00699                 Bit16u submap_cp;
00700 
00701                 // read codepage of submapping
00702                 submap_cp=host_readw(&read_buf[start_pos+0x14u+sub_map*8u]);
00703 
00704                 if (submap_cp!=0) return submap_cp;
00705         }
00706         return (IS_PC98_ARCH ? 932 : 437);
00707 }
00708 
00709 Bitu keyboard_layout::read_codepage_file(const char* codepage_file_name, Bit32s codepage_id) {
00710         char cp_filename[512];
00711         strcpy(cp_filename, codepage_file_name);
00712         if (!strcmp(cp_filename,"none")) return KEYB_NOERROR;
00713 
00714         if (codepage_id==dos.loaded_codepage) return KEYB_NOERROR;
00715 
00716         if (!strcmp(cp_filename,"auto")) {
00717                 // select matching .cpi-file for specified codepage
00718                 switch (codepage_id) {
00719                         case 437:       case 850:       case 852:       case 853:       case 857:       case 858:       
00720                                                 sprintf(cp_filename, "EGA.CPI"); break;
00721                         case 775:       case 859:       case 1116:      case 1117:
00722                                                 sprintf(cp_filename, "EGA2.CPI"); break;
00723                         case 771:       case 772:       case 808:       case 855:       case 866:       case 872:
00724                                                 sprintf(cp_filename, "EGA3.CPI"); break;
00725                         case 848:       case 849:       case 1125:      case 1131:      case 61282:
00726                                                 sprintf(cp_filename, "EGA4.CPI"); break;
00727                         case 737:       case 851:       case 869:
00728                                                 sprintf(cp_filename, "EGA5.CPI"); break;
00729                         case 113:       case 899:       case 59829:     case 60853:
00730                                                 sprintf(cp_filename, "EGA6.CPI"); break;
00731                         case 58152:     case 58210:     case 59234:     case 60258:     case 62306:
00732                                                 sprintf(cp_filename, "EGA7.CPI"); break;
00733                         case 770:       case 773:       case 774:       case 777:       case 778:
00734                                                 sprintf(cp_filename, "EGA8.CPI"); break;
00735                         case 860:       case 861:       case 863:       case 865:
00736                                                 sprintf(cp_filename, "EGA9.CPI"); break;
00737                         case 667:       case 668:       case 790:       case 867:       case 991:       case 57781:
00738                                                 sprintf(cp_filename, "EGA10.CPI"); break;
00739                         default:
00740                                 LOG_MSG("No matching cpi file for codepage %i",codepage_id);
00741                                 return KEYB_INVALIDCPFILE;
00742                 }
00743         }
00744 
00745         Bit32u start_pos;
00746         Bit16u number_of_codepages;
00747 
00748         char nbuf[512];
00749         sprintf(nbuf, "%s", cp_filename);
00750         FILE* tempfile=OpenDosboxFile(nbuf);
00751         if (tempfile==NULL) {
00752                 size_t strsz=strlen(nbuf);
00753                 if (strsz) {
00754                         char plc=(char)toupper(*reinterpret_cast<unsigned char*>(&nbuf[strsz-1]));
00755                         if (plc=='I') {
00756                                 // try CPX-extension as well
00757                                 nbuf[strsz-1]='X';
00758                                 tempfile=OpenDosboxFile(nbuf);
00759                         } else if (plc=='X') {
00760                                 // try CPI-extension as well
00761                                 nbuf[strsz-1]='I';
00762                                 tempfile=OpenDosboxFile(nbuf);
00763                         }
00764                 }
00765         }
00766 
00767         static Bit8u cpi_buf[65536];
00768         Bit32u cpi_buf_size=0,size_of_cpxdata=0;
00769         bool upxfound=false;
00770         Bit16u found_at_pos=5;
00771         if (tempfile==NULL) {
00772                 // check if build-in codepage is available
00773                 switch (codepage_id) {
00774                         case 437:       case 850:       case 852:       case 853:       case 857:       case 858:       
00775                                                 for (Bitu bct=0; bct<6322; bct++) cpi_buf[bct]=font_ega_cpx[bct];
00776                                                 cpi_buf_size=6322;
00777                                                 break;
00778                         case 771:       case 772:       case 808:       case 855:       case 866:       case 872:
00779                                                 for (Bitu bct=0; bct<5455; bct++) cpi_buf[bct]=font_ega3_cpx[bct];
00780                                                 cpi_buf_size=5455;
00781                                                 break;
00782                         case 737:       case 851:       case 869:
00783                                                 for (Bitu bct=0; bct<5720; bct++) cpi_buf[bct]=font_ega5_cpx[bct];
00784                                                 cpi_buf_size=5720;
00785                                                 break;
00786                         default: 
00787                                 return KEYB_INVALIDCPFILE;
00788                                 break;
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                 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