Index: dosbox-0.74/src/dos/dos_keyboard_layout.cpp
===================================================================
--- dosbox-0.74.orig/src/dos/dos_keyboard_layout.cpp	2010-05-10 19:43:54.000000000 +0200
+++ dosbox-0.74/src/dos/dos_keyboard_layout.cpp	2010-10-19 01:04:52.000000000 +0200
@@ -1,5 +1,6 @@
 /*
- *  Copyright (C) 2002-2010  The DOSBox Team
+ *  Copyright (C) 2002-2008 The DOSBox Team
+ *  Copyright (C) 2009-2010	Javier S. Pedro
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -16,13 +17,18 @@
  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
-/* $Id: dos_keyboard_layout.cpp,v 1.22 2009-09-06 19:25:33 c2woody Exp $ */
+/* $Id: dos_keyboard_layout.cpp,v 1.11 2008/01/17 18:53:18 c2woody Exp $ */
+
+/* This file was heavily modified by javispedro. It is a rewrite of the original
+ * implementation, with more keyb features supported (that I need for the N8x0
+ * layouts), less memory footprint and possibly more bugs.
+ */
 
 #include "dosbox.h"
 #include "bios.h"
-#include "bios_disk.h"
 #include "setup.h"
 #include "support.h"
+#include "mem.h"
 #include "../ints/int10.h"
 #include "regs.h"
 #include "callback.h"
@@ -33,672 +39,899 @@
 #include "dos_codepages.h"
 #include "dos_keyboard_layout_data.h"
 
-#if defined (WIN32)
-#include <windows.h>
-#endif
+#include <inttypes.h>
 
+#include <cctype>
+#include <vector>
+#include <string>
+#include <fstream>
+#include <algorithm>
+
+//#define KEYB_DEBUG(format, ...) printf("KEYB: " format "\n" , ##__VA_ARGS__)
+#define KEYB_DEBUG(...) /* disabled */
+#define KEYB_WARN(format, ...) printf("KEYB: " format "\n" , ##__VA_ARGS__)
+
+#define KEYB_DEBUG_POS(stream) KEYB_DEBUG("Stream pos %lu (%lx)", \
+								(unsigned long) (stream.tellg()), \
+								(unsigned long) (stream.tellg()));
+
+typedef int err_t;
+
+using std::vector;
+using std::string;
+using std::ios;
+using std::istream;
+using std::ifstream;
+using std::streambuf;
+using std::min;
+
+static inline uint16_t HostReadUInt16(void * p)
+{
+	return host_readw(reinterpret_cast<Bit8u*>(p));
+};
+
+class arraybuf : public streambuf
+{
+public:
+	arraybuf(uint8_t* array, size_t size)
+	{
+		char * b = reinterpret_cast<char*>(array);
+		char * n = b;
+		char * e = b + size;
+		setg(b, n, e);
+	};
 
-static FILE* OpenDosboxFile(const char* name) {
-	Bit8u drive;
-	char fullname[DOS_PATHLENGTH];
+	pos_type seekpos(pos_type pos, ios::openmode mode = ios::in | ios::out)
+	{
+		char * b = eback();
+		char * n = b + pos;
+		char * e = egptr();
 
-	localDrive* ldp=0;
-	// try to build dos name
-	if (DOS_MakeName(name,fullname,&drive)) {
-		try {
-			// try to open file on mounted drive first
-			ldp=dynamic_cast<localDrive*>(Drives[drive]);
-			if (ldp) {
-				FILE *tmpfile=ldp->GetSystemFilePtr(fullname, "rb");
-				if (tmpfile != NULL) return tmpfile;
-			}
+		if (n > e) {
+			return -1;
 		}
-		catch(...) {}
-	}
-	FILE *tmpfile=fopen(name, "rb");
-	return tmpfile;
-}
 
+		setg(b, n, e);
+		return n - b;
+	};
 
-class keyboard_layout {
+	pos_type seekoff(off_type off, ios::seekdir way,
+	      ios::openmode mode = ios::in | ios::out)
+	{
+		char * b = eback();
+		char * n = gptr();
+		char * e = egptr();
+
+		switch (way) {
+		case ios::beg:
+			n = b + off;
+			break;
+		case ios::cur:
+			n = n + off;
+			break;
+		case ios::end:
+			n = e + off;
+			break;
+		default:
+			return -1;
+		}
+
+		setg(b, n, e);
+		return n - b;
+	};
+};
+
+class aistream : public istream
+{
+	arraybuf _abuf;
 public:
-	keyboard_layout() {
-		this->reset();
-		language_codes=NULL;
-		use_foreign_layout=false;
-		sprintf(current_keyboard_file_name, "none");
+	aistream(uint8_t* array, size_t size)
+	: _abuf(array, size)
+	{
+		this->init(&_abuf);
 	};
+};
 
-	~keyboard_layout();
+Bitu DOS_LoadKeyboardLayout(const char * layoutFile, const char * layoutName,
+						const char * codepage, const char * codepageFileName);
+const char* DOS_GetLoadedLayout(void);
+static bool searchDosFile(const char* name, char* fullName);
 
-	// read in a codepage from a .cpi-file
-	Bitu read_codepage_file(const char* codepage_file_name, Bit32s codepage_id);
-	Bit16u extract_codepage(const char* keyboard_file_name);
-	// read in a keyboard layout from a .kl-file
-	Bitu read_keyboard_file(const char* keyboard_file_name, Bit32s req_cp);
-
-	// call layout_key to apply the current language layout
-	bool layout_key(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3);
-
-	Bitu switch_keyboard_layout(const char* new_layout, keyboard_layout* &created_layout, Bit32s& tried_cp);
-	void switch_foreign_layout();
-	const char* get_layout_name();
-	const char* main_language_code();
+class KeyboardLayout {
+public:
+	KeyboardLayout();
+	~KeyboardLayout();
 
+	/** Loads a layout from a specific KLib file
+	 *	If layout == NULL, load a KL file
+	 */
+	err_t load(const char * fileName, const char * layout = NULL);
+	/** Loads a layout from a specific iostream */
+	err_t load(const char * fileName, istream& file, const char * layout = NULL);
+
+	/** Gets "default" codepage for the loaded layout */
+	int getCodepage();
+	/** Switches to the appropiate submapping for codepage */
+	void selectCodepage(int codepage);
+
+	bool handleKey(unsigned int key,
+					uint8_t flags, uint8_t flags2, uint8_t flags3);
+
+	const string& getLoadedFile();
+	const string& getLoadedLayout();
+
+private:
+	// KL-file structures
+
+	struct keybcb {
+		uint8_t		numSubmappings;
+		uint8_t		numPlanes;
+		char		decimalSep;
+		uint8_t		curSubmapping;
+		uint16_t	mcbId;
+		uint16_t	flags;
+		uint16_t	id;
+		char		name[8];
+	} __attribute__((__packed__));
+
+	struct submapping {
+		uint16_t	codepage;
+		uint16_t	tableOff;
+		uint16_t	diacriticOff;
+		uint16_t	stringOff;
+	} __attribute__((__packed__));
+
+	struct plane {
+		uint16_t	required;
+		uint16_t	forbidden;
+		uint16_t	userRequired;
+		uint16_t	userForbidden;
+	} __attribute__((__packed__));
+
+	struct keyitem {
+		uint8_t	scancode;
+		uint8_t	flags;
+		uint8_t command;
+		union {
+			uint8_t bytes[];
+			uint16_t words[];
+		} __attribute__((__packed__));
+	} __attribute__((__packed__));
+
+	struct diacritic {
+		char		individual;
+		uint8_t		numPairs;
+		struct {
+			char	test;
+			char	produce;
+		} __attribute__((__packed__)) pairs[];
+	} __attribute__((__packed__));
+
+	struct kstring {
+		uint8_t		numPairs;
+		uint16_t	pairs[];
+	} __attribute__((__packed__));
 
 private:
-	static const Bit8u layout_pages=12;
-	Bit16u current_layout[(MAX_SCAN_CODE+1)*layout_pages];
-	struct {
-		Bit16u required_flags,forbidden_flags;
-		Bit16u required_userflags,forbidden_userflags;
-	} current_layout_planes[layout_pages-4];
-	Bit8u additional_planes,used_lock_modifiers;
-
-	// diacritics table
-	Bit8u diacritics[2048];
-	Bit16u diacritics_entries;
-	Bit16u diacritics_character;
-	Bit16u user_keys;
-
-	char current_keyboard_file_name[256];
-	bool use_foreign_layout;
-
-	// language code storage used when switching layouts
-	char** language_codes;
-	Bitu language_code_count;
-
-	void reset();
-	void read_keyboard_file(Bit32s specific_layout);
-	Bitu read_keyboard_file(const char* keyboard_file_name, Bit32s specific_layout, Bit32s requested_codepage);
-	bool map_key(Bitu key, Bit16u layouted_key, bool is_command, bool is_keypair);
+	string _fileName;	// Current loaded file path
+	string _layout; 	// Current loaded layout name
+
+	uint8_t* _data;			// Working copy of layout data
+
+	uint16_t _userStatus;	// User keys flags
+
+	diacritic* _curDiacritic;	// Active diacritic or 0
+
+	err_t loadLibrary(istream& file, const char * layout);
+	err_t loadLayout(istream& file);
+
+	inline keybcb* getKeybCB() {
+		return reinterpret_cast<keybcb*>(_data);
+	}
+
+	inline submapping* getSubmapping(int i) {
+		unsigned int l = 20 + i * 8;
+		return reinterpret_cast<submapping*>(&_data[l]);
+	}
+
+	inline plane* getPlane(int i) {
+		keybcb* cb = getKeybCB();
+		unsigned int l = 20 + cb->numSubmappings * 8 + i * 8;
+		return reinterpret_cast<plane*>(&_data[l]);
+	}
+
+	uint16_t getCurrentStatus(uint8_t flags, uint8_t flags2, uint8_t flags3);
+	int getPlaneFor(uint16_t status, uint16_t userStatus);
+	keyitem* lookupKeyTable(submapping* s, unsigned int scan);
+	diacritic* lookupDiacritic(submapping* s, int di);
+	diacritic* lookupDiacritic(int di);
+	kstring* lookupString(submapping* s, int si);
+	kstring* lookupString(int si);
+	bool doKey(keyitem* k, int planeNum, uint16_t status);
+	bool doDiacritic(diacritic* d, uint16_t data);
+	bool doCommand(unsigned int command);
+	bool doString(int string);
+
+	inline void sendKey(uint16_t key, bool doDiacritics = true) {
+		if (_curDiacritic && doDiacritics) {
+			// handle pending diacritic
+			KEYB_DEBUG("Pending diacritic while sending 0x%x (%c)",	key, key);
+			bool handled = doDiacritic(_curDiacritic, key);
+			_curDiacritic = 0;
+			if (handled)
+				return;
+		}
+		if (key) {
+			KEYB_DEBUG("Sending 0x%x (%c)",	key, key);
+			BIOS_AddKeyToBuffer(key);
+		}
+	}
+};
+
+KeyboardLayout::KeyboardLayout()
+	: _fileName(), _layout(), _data(0), _userStatus(0), _curDiacritic(0)
+{
+
 };
 
+KeyboardLayout::~KeyboardLayout()
+{
+	if (_data)
+		delete[] _data;
+};
 
-keyboard_layout::~keyboard_layout() {
-	if (language_codes) {
-		for (Bitu i=0; i<language_code_count; i++)
-			delete[] language_codes[i];
-		delete[] language_codes;
-		language_codes=NULL;
+err_t KeyboardLayout::load(const char * fileName, const char * layout)
+{
+	ifstream file(fileName, ios::in & ios::binary);
+	if (!file.good()) {
+		KEYB_WARN("Can't open %s", fileName);
+		return KEYB_FILENOTFOUND; // File not found or couldn't open
 	}
-}
 
-void keyboard_layout::reset() {
-	for (Bit32u i=0; i<(MAX_SCAN_CODE+1)*layout_pages; i++) current_layout[i]=0;
-	for (Bit32u i=0; i<layout_pages-4; i++) {
-		current_layout_planes[i].required_flags=0;
-		current_layout_planes[i].forbidden_flags=0xffff;
-		current_layout_planes[i].required_userflags=0;
-		current_layout_planes[i].forbidden_userflags=0xffff;
-	}
-	used_lock_modifiers=0x0f;
-	diacritics_entries=0;		// no diacritics loaded
-	diacritics_character=0;
-	user_keys=0;				// all userkeys off
-	language_code_count=0;
-}
+	return load(fileName, file, layout);
+};
 
-Bitu keyboard_layout::read_keyboard_file(const char* keyboard_file_name, Bit32s req_cp) {
-	return this->read_keyboard_file(keyboard_file_name, -1, req_cp);
-}
+err_t KeyboardLayout::load
+(const char * fileName, istream& file, const char * layout)
+{
+	err_t err = KEYB_NOERROR;
+	char buffer[4];
 
-// switch to a different layout
-void keyboard_layout::read_keyboard_file(Bit32s specific_layout) {
-	if (strcmp(current_keyboard_file_name,"none"))
-		this->read_keyboard_file(current_keyboard_file_name, specific_layout, dos.loaded_codepage);
-}
+	if (layout) {
+		KEYB_DEBUG("Searching %s for layout %s...", fileName, layout);
+	} else {
+		KEYB_DEBUG("Loading %s...", fileName);
+	}
 
-static Bit32u read_kcl_file(const char* kcl_file_name, const char* layout_id, bool first_id_only) {
-	FILE* tempfile = OpenDosboxFile(kcl_file_name);
-	if (tempfile==0) return 0;
-
-	static Bit8u rbuf[8192];
-
-	// check ID-bytes of file
-	Bit32u dr=(Bit32u)fread(rbuf, sizeof(Bit8u), 7, tempfile);
-	if ((dr<7) || (rbuf[0]!=0x4b) || (rbuf[1]!=0x43) || (rbuf[2]!=0x46)) {
-		fclose(tempfile);
-		return 0;
+	// Search signature (assuming file pointer positioned at start of file)
+	file.read(buffer, 4);
+
+	if (buffer[0]==0x4b && buffer[1]==0x43 && buffer[2]==0x46) {
+		// KLib
+		if (layout) {
+			err = loadLibrary(file, layout);
+		} else {
+			// If a layout name has not been specified, we do nothing.
+			KEYB_WARN("%s: Library file, but no layoutName", fileName);
+			err = KEYB_LAYOUTNOTFOUND;
+		}
+	} else if (buffer[0]==0x4b && buffer[1]==0x4c && buffer[2]==0x46) {
+		// KL
+		if (layout) {
+			KEYB_WARN("%s: Ignoring layout name %s", fileName, layout);
+			layout = 0;
+		}
+
+		err = loadLayout(file);
+	} else {
+		KEYB_WARN("%s: Unknown file type", fileName);
+		err = KEYB_INVALIDFILE;
 	}
 
-	fseek(tempfile, 7+rbuf[6], SEEK_SET);
+	if (err) {
+		if (_data)
+			delete[] _data;
+	} else {
+		_fileName = fileName;
+		if (layout)
+			_layout = layout;
 
-	for (;;) {
-		Bit32u cur_pos=(Bit32u)(ftell(tempfile));
-		dr=(Bit32u)fread(rbuf, sizeof(Bit8u), 5, tempfile);
-		if (dr<5) break;
-		Bit16u len=host_readw(&rbuf[0]);
-
-		Bit8u data_len=rbuf[2];
-
-		char lng_codes[258];
-		fseek(tempfile, -2, SEEK_CUR);
-		// get all language codes for this layout
-		for (Bitu i=0; i<data_len;) {
-			fread(rbuf, sizeof(Bit8u), 2, tempfile);
-			Bit16u lcnum=host_readw(&rbuf[0]);
-			i+=2;
-			Bitu lcpos=0;
-			for (;i<data_len;) {
-				fread(rbuf, sizeof(Bit8u), 1, tempfile);
-				i++;
-				if (((char)rbuf[0])==',') break;
-				lng_codes[lcpos++]=(char)rbuf[0];
-			}
-			lng_codes[lcpos]=0;
-			if (strcasecmp(lng_codes, layout_id)==0) {
-				// language ID found in file, return file position
-				fclose(tempfile);
-				return cur_pos;
-			}
-			if (first_id_only) break;
-			if (lcnum) {
-				sprintf(&lng_codes[lcpos],"%d",lcnum);
-				if (strcasecmp(lng_codes, layout_id)==0) {
-					// language ID found in file, return file position
-					return cur_pos;
+		KEYB_DEBUG("%s: Loaded layout %s", fileName, _layout.c_str());
+	}
+
+	return err;
+};
+
+err_t KeyboardLayout::loadLibrary(istream& file, const char * layout)
+{
+	long pos;
+	// Position file pointer at first item
+	file.seekg(2, ios::cur);
+	pos = file.peek() + 7;
+
+	KEYB_DEBUG_POS(file);
+
+	for(;;) {
+		char buffer[8];
+
+		file.seekg(pos, ios::beg);
+		if (!file.good())
+			break;
+
+		file.read(buffer, 3);
+
+		uint16_t fileLen = HostReadUInt16(&buffer[0]);
+		const int nameStringLen = buffer[2];
+		int remStringLen = nameStringLen; // remaining
+
+		while (remStringLen > 0) {
+			int layoutId;
+
+			KEYB_DEBUG_POS(file);
+
+			KEYB_DEBUG("Before remStringLen = %d", remStringLen);
+
+			// Read keyboard numeric identifier (may be 0)
+			file.read(buffer, 2);
+			remStringLen -= 2;
+			layoutId = HostReadUInt16(&buffer[0]);
+
+			// Read keyboard layout name
+			file.get(buffer, min(remStringLen + 1, 8), ',');
+			KEYB_DEBUG("max = %d, gcount = %d", min(remStringLen + 1, 8), (int) file.gcount());
+			remStringLen -= file.gcount();
+
+			KEYB_DEBUG("Found layout %d %s in file",
+				layoutId, buffer);
+
+			// Check if matches
+			if (strcasecmp(buffer, layout) == 0) {
+				// Layout found !
+				const long loadOff = pos + 3 + nameStringLen;
+				const long loadLen = fileLen - nameStringLen;
+				KEYB_DEBUG("Loading %d %s...\n\toff=0x%lx len=%ld",
+					layoutId, buffer, loadOff, loadLen);
+
+				// Seek to start of KeybCB
+				file.seekg(loadOff, ios::beg);
+				_data = new uint8_t[loadLen];
+
+				// Read the whole KL file
+				file.read(reinterpret_cast<char*>(_data), loadLen);
+
+				KEYB_DEBUG("%x %x %x ... %x %x %x",
+					 _data[0], _data[1], _data[2],
+					 _data[loadLen - 3], _data[loadLen - 2], _data[loadLen - 1]);
+
+				if (file.fail()) {
+					KEYB_WARN("Unexpected end of file");
+					return KEYB_INVALIDFILE;
 				}
+
+				return KEYB_NOERROR;
+			}
+
+			KEYB_DEBUG("After remStringLen = %d", remStringLen);
+
+			if (file.peek() == ',') {
+				file.ignore();
+				remStringLen--;
+			} else {
+				break; //Out of namestring
 			}
 		}
-		fseek(tempfile, cur_pos+3+len, SEEK_SET);
+
+		pos += 3+fileLen; // Next KL file
+
+	}
+
+	KEYB_WARN("Layout %s not found", layout);
+
+	return KEYB_LAYOUTNOTFOUND;
+};
+
+err_t KeyboardLayout::loadLayout(istream& file)
+{
+	char buffer[8];
+	file.seekg(5); // KL header off5: Length n of the following namestring
+
+	const int nameStringLen = file.get();
+	int remStringLen = nameStringLen;
+	int layoutId;
+
+	// Try and read first keyboard layout name
+	if (remStringLen > 0) {
+		KEYB_DEBUG_POS(file);
+
+		KEYB_DEBUG("Before remStringLen = %d", remStringLen);
+
+		// Keyboard numeric identifier
+		file.read(buffer, 2);
+		remStringLen -= 2;
+		layoutId = HostReadUInt16(&buffer[0]);
+
+		// Keyboard layout name
+		file.get(buffer, min(remStringLen + 1, 8), ',');
+		KEYB_DEBUG("max = %d, gcount = %d", min(remStringLen + 1, 8), (int) file.gcount());
+		remStringLen -= file.gcount();
+
+		KEYB_DEBUG("Found layout %d %s in file",
+			layoutId, buffer);
+
+		_layout = buffer;
+
+		KEYB_DEBUG("After remStringLen = %d", remStringLen);
+	} else {
+		strcpy(buffer, "null");
+		_layout = "null";
+		layoutId = 0;
+	}
+
+	file.seekg(0, ios::end);
+	const long fileLen = file.tellg();
+	const long loadOff = 6 + nameStringLen;
+	const long loadLen = fileLen - loadOff;
+	KEYB_DEBUG("Loading %d %s...\n\toff=0x%lx len=%ld",
+		layoutId, buffer, loadOff, loadLen);
+
+	// Seek to start of KeybCB
+	file.seekg(loadOff, ios::beg);
+	_data = new uint8_t[loadLen];
+
+	// Read the whole KL file
+	file.read(reinterpret_cast<char*>(_data), loadLen);
+
+	KEYB_DEBUG("%x %x %x ... %x %x %x",
+		 _data[0], _data[1], _data[2],
+		 _data[loadLen - 3], _data[loadLen - 2], _data[loadLen - 1]);
+
+	if (file.fail()) {
+		KEYB_WARN("Unexpected end of file");
+		return KEYB_INVALIDFILE;
+	}
+
+	return KEYB_NOERROR;
+};
+
+int KeyboardLayout::getCodepage()
+{
+	keybcb* cb = getKeybCB();
+
+	KEYB_DEBUG("Num submappings = %d", cb->numSubmappings);
+
+	for (int i = 0; i < cb->numSubmappings; i++) {
+		submapping* s = getSubmapping(i);
+		uint16_t codepage = HostReadUInt16(&s->codepage);
+
+		KEYB_DEBUG("Submapping %d, codepage = %d", i, codepage);
+
+		if (codepage)
+			return codepage;
 	}
 
-	fclose(tempfile);
 	return 0;
 }
 
-static Bit32u read_kcl_data(Bit8u * kcl_data, Bit32u kcl_data_size, const char* layout_id, bool first_id_only) {
-	// check ID-bytes
-	if ((kcl_data[0]!=0x4b) || (kcl_data[1]!=0x43) || (kcl_data[2]!=0x46)) {
-		return 0;
-	}
+void KeyboardLayout::selectCodepage(int newCodepage)
+{
+	keybcb* cb = getKeybCB();
+	int newSubmapping = 0;
 
-	Bit32u dpos=7+kcl_data[6];
+	for (int i = 0; i < cb->numSubmappings; i++) {
+		submapping* s = getSubmapping(i);
+		uint16_t codepage = HostReadUInt16(&s->codepage);
 
-	for (;;) {
-		if (dpos+5>kcl_data_size) break;
-		Bit32u cur_pos=dpos;
-		Bit16u len=host_readw(&kcl_data[dpos]);
-		Bit8u data_len=kcl_data[dpos+2];
-		dpos+=5;
-
-		char lng_codes[258];
-		// get all language codes for this layout
-		for (Bitu i=0; i<data_len;) {
-			Bit16u lcnum=host_readw(&kcl_data[dpos-2]);
-			i+=2;
-			Bitu lcpos=0;
-			for (;i<data_len;) {
-				if (dpos+1>kcl_data_size) break;
-				char lc=(char)kcl_data[dpos];
-				dpos++;
-				i++;
-				if (lc==',') break;
-				lng_codes[lcpos++]=lc;
-			}
-			lng_codes[lcpos]=0;
-			if (strcasecmp(lng_codes, layout_id)==0) {
-				// language ID found, return position
-				return cur_pos;
-			}
-			if (first_id_only) break;
-			if (lcnum) {
-				sprintf(&lng_codes[lcpos],"%d",lcnum);
-				if (strcasecmp(lng_codes, layout_id)==0) {
-					// language ID found, return position
-					return cur_pos;
-				}
-			}
-			dpos+=2;
+		if (codepage == newCodepage) {
+			newSubmapping = i;
+			break;
 		}
-		dpos=cur_pos+3+len;
 	}
-	return 0;
+
+	// Select submapping
+	submapping* s = getSubmapping(newSubmapping);
+	KEYB_DEBUG("Selecting submapping %d with codepage %d",
+				newSubmapping, HostReadUInt16(&s->codepage));
+
+	KEYB_DEBUG("New submapping: table=%x, diacritic=%x, string=%x",
+			s->tableOff, s->diacriticOff, s->stringOff);
+
+	cb->curSubmapping = newSubmapping;
 }
 
-Bitu keyboard_layout::read_keyboard_file(const char* keyboard_file_name, Bit32s specific_layout, Bit32s requested_codepage) {
-	this->reset();
+bool KeyboardLayout::handleKey
+(unsigned int key, uint8_t flags, uint8_t flags2, uint8_t flags3)
+{
+	keybcb* cb = getKeybCB();
+	bool handled = false;
 
-	if (specific_layout==-1) strcpy(current_keyboard_file_name, keyboard_file_name);
-	if (!strcmp(keyboard_file_name,"none")) return KEYB_NOERROR;
+	// Select plane
+	uint16_t status = getCurrentStatus(flags, flags2, flags3);
+	int planeNum = getPlaneFor(status, _userStatus);
+	KEYB_DEBUG("Calculated plane = %d", planeNum);
 
-	static Bit8u read_buf[65535];
-	Bit32u read_buf_size, read_buf_pos, bytes_read;
-	Bit32u start_pos=5;
+	// Try active submapping
+	if (cb->curSubmapping) {
+		submapping* s = getSubmapping(cb->curSubmapping);
+		keyitem* k = lookupKeyTable(s, key);
 
-	char nbuf[512];
-	read_buf_size = 0;
-	sprintf(nbuf, "%s.kl", keyboard_file_name);
-	FILE* tempfile = OpenDosboxFile(nbuf);
-	if (tempfile==NULL) {
-		// try keyboard layout libraries next
-		if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,true))) {
-			tempfile = OpenDosboxFile("keyboard.sys");
-		} else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,true))) {
-			tempfile = OpenDosboxFile("keybrd2.sys");
-		} else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,true))) {
-			tempfile = OpenDosboxFile("keybrd3.sys");
-		} else if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,false))) {
-			tempfile = OpenDosboxFile("keyboard.sys");
-		} else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,false))) {
-			tempfile = OpenDosboxFile("keybrd2.sys");
-		} else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,false))) {
-			tempfile = OpenDosboxFile("keybrd3.sys");
-		} else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,true))) {
-			read_buf_size=0;
-			for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
-		} else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,true))) {
-			read_buf_size=0;
-			for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
-		} else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,true))) {
-			read_buf_size=0;
-			for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
-		} else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,false))) {
-			read_buf_size=0;
-			for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
-		} else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,false))) {
-			read_buf_size=0;
-			for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
-		} else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,false))) {
-			read_buf_size=0;
-			for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
-		} else {
-			LOG(LOG_BIOS,LOG_ERROR)("Keyboard layout file %s not found",keyboard_file_name);
-			return KEYB_FILENOTFOUND;
-		}
-		if (tempfile) {
-			fseek(tempfile, start_pos+2, SEEK_SET);
-			read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
-			fclose(tempfile);
+		if (k) {
+			handled = doKey(k, planeNum, status);
 		}
-		start_pos=0;
+	}
+
+	if (handled)
+		return true;
+	KEYB_DEBUG("Not found/handled in active submapping");
+
+
+	// Try normal submapping
+	submapping* s = getSubmapping(0);
+	keyitem* k = lookupKeyTable(s, key);
+
+	if (k) {
+		handled = doKey(k, planeNum, status);
+	}
+
+	if (handled)
+		return true;
+	KEYB_DEBUG("Not found/handled in normal submapping");
+
+	// Not handled
+	if (key & 0x80) // if breakcode, do not handle diacritic
+		return false;
+
+	if (key == 42 || key == 54 || key == 29 || key == 157 || key == 56 ||
+		key == 184 || key == 58)
+		return false; // avoid: shifts, ctrls, alts, caps
+
+	// Rest: handle pending diacritics
+	sendKey(0, true);
+
+	return false;
+};
+
+const string& KeyboardLayout::getLoadedLayout()
+{
+	return _layout;
+};
+
+uint16_t KeyboardLayout::getCurrentStatus
+(uint8_t flags, uint8_t flags2, uint8_t flags3)
+{
+	uint16_t status = (flags & 0x7F) | ((flags2 & 0x3) << 8);
+	// Missing up so far: Right Control & Alt, E0 prefixed key, Either shift
+
+	// Right Control & Alt
+	status |= (flags3 & 0xC) << 8;
+
+	// E0 prefixed key
+	status |= (flags3 & 0x2) << (12 - 1);
+
+	// Either shift
+	status |= flags & 0x3 ? 1 << 14 : 0;
+
+	return status;
+};
+
+int KeyboardLayout::getPlaneFor(uint16_t status, uint16_t userStatus)
+{
+	KEYB_DEBUG("%s %x %x", __func__, status, userStatus);
+
+	// TODO: If any of the remaining planes uses the CapsLock (resp. NumLock,
+    //     ScrollLock) at all, then CapsLock (resp. NumLock, ScrollLock) must
+    //     be excluded too.
+
+	if (!userStatus && ((status & 0x4F0F) == 0)) {
+		// PLANE 1: INCLUDE:  -     EXCLUDE: Shift, Ctrl, Alt, UserKeys
+		return 0;
+	} else if (!userStatus && ((status & 0xF0C) == 0) && (status & 0x4000)) {
+		// PLANE 2: INCLUDE: Shift  EXCLUDE: Ctrl, Alt, UserKeys
+		return 1;
 	} else {
-		// check ID-bytes of file
-		Bit32u dr=(Bit32u)fread(read_buf, sizeof(Bit8u), 4, tempfile);
-		if ((dr<4) || (read_buf[0]!=0x4b) || (read_buf[1]!=0x4c) || (read_buf[2]!=0x46)) {
-			LOG(LOG_BIOS,LOG_ERROR)("Invalid keyboard layout file %s",keyboard_file_name);
-			return KEYB_INVALIDFILE;
-		}
-		
-		fseek(tempfile, 0, SEEK_SET);
-		read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
-		fclose(tempfile);
-	}
-
-	Bit8u data_len,submappings;
-	data_len=read_buf[start_pos++];
-
-	language_codes=new char*[data_len];
-	language_code_count=0;
-	// get all language codes for this layout
-	for (Bitu i=0; i<data_len;) {
-		language_codes[language_code_count]=new char[256];
-		i+=2;
-		Bitu lcpos=0;
-		for (;i<data_len;) {
-			char lcode=char(read_buf[start_pos+i]);
-			i++;
-			if (lcode==',') break;
-			language_codes[language_code_count][lcpos++]=lcode;
-		}
-		language_codes[language_code_count][lcpos]=0;
-		language_code_count++;
-	}
-
-	start_pos+=data_len;		// start_pos==absolute position of KeybCB block
-
-	submappings=read_buf[start_pos];
-	additional_planes=read_buf[start_pos+1];
-
-	// four pages always occupied by normal,shift,flags,commandbits
-	if (additional_planes>(layout_pages-4)) additional_planes=(layout_pages-4);
-
-	// seek to plane descriptor
-	read_buf_pos=start_pos+0x14+submappings*8;
-	for (Bit16u cplane=0; cplane<additional_planes; cplane++) {
-		Bit16u plane_flags;
-
-		// get required-flags (shift/alt/ctrl-states etc.)
-		plane_flags=host_readw(&read_buf[read_buf_pos]);
-		read_buf_pos+=2;
-		current_layout_planes[cplane].required_flags=plane_flags;
-		used_lock_modifiers |= (plane_flags&0x70);
-		// get forbidden-flags
-		plane_flags=host_readw(&read_buf[read_buf_pos]);
-		read_buf_pos+=2;
-		current_layout_planes[cplane].forbidden_flags=plane_flags;
-
-		// get required-userflags
-		plane_flags=host_readw(&read_buf[read_buf_pos]);
-		read_buf_pos+=2;
-		current_layout_planes[cplane].required_userflags=plane_flags;
-		// get forbidden-userflags
-		plane_flags=host_readw(&read_buf[read_buf_pos]);
-		read_buf_pos+=2;
-		current_layout_planes[cplane].forbidden_userflags=plane_flags;
-	}
-
-	bool found_matching_layout=false;
-	
-	// check all submappings and use them if general submapping or same codepage submapping
-	for (Bit16u sub_map=0; (sub_map<submappings) && (!found_matching_layout); sub_map++) {
-		Bit16u submap_cp, table_offset;
-
-		if ((sub_map!=0) && (specific_layout!=-1)) sub_map=(Bit16u)(specific_layout&0xffff);
-
-		// read codepage of submapping
-		submap_cp=host_readw(&read_buf[start_pos+0x14+sub_map*8]);
-		if ((submap_cp!=0) && (submap_cp!=requested_codepage) && (specific_layout==-1))
-			continue;		// skip nonfitting submappings
-
-		if (submap_cp==requested_codepage) found_matching_layout=true;
-
-		// get table offset
-		table_offset=host_readw(&read_buf[start_pos+0x18+sub_map*8]);
-		diacritics_entries=0;
-		if (table_offset!=0) {
-			// process table
-			Bit16u i,j;
-			for (i=0; i<2048;) {
-				if (read_buf[start_pos+table_offset+i]==0) break;	// end of table
-				diacritics_entries++;
-				i+=read_buf[start_pos+table_offset+i+1]*2+2;
-			}
-			// copy diacritics table
-			for (j=0; j<=i; j++) diacritics[j]=read_buf[start_pos+table_offset+j];
+		// Check user defined planes
+		keybcb* cb = getKeybCB();
+		for (int i = 0; i < cb->numPlanes; i++) {
+			plane* p = getPlane(i);
+			uint16_t required = HostReadUInt16(&p->required);
+			uint16_t forbidden = HostReadUInt16(&p->forbidden);
+			uint16_t userRequired = HostReadUInt16(&p->userRequired);
+			uint16_t userForbidden = HostReadUInt16(&p->userForbidden);
+
+			//KEYB_DEBUG(" %x %x  %x %x", required, forbidden,
+			//	userRequired, userForbidden);
+
+			bool match = (status & required) == required &&
+						 (status & forbidden) == 0;
+			bool userMatch = (userStatus & userRequired) == userRequired &&
+							 (userStatus & userForbidden) == 0;
+
+			//KEYB_DEBUG(" %d %d", match, userMatch);
+
+			if (match && userMatch)
+				return i + 2;
 		}
+	}
 
+	KEYB_DEBUG(" Unknown plane");
 
-		// get table offset
-		table_offset=host_readw(&read_buf[start_pos+0x16+sub_map*8]);
-		if (table_offset==0) continue;	// non-present table
-
-		read_buf_pos=start_pos+table_offset;
-
-		bytes_read=read_buf_size-read_buf_pos;
-
-		// process submapping table
-		for (Bit32u i=0; i<bytes_read;) {
-			Bit8u scan=read_buf[read_buf_pos++];
-			if (scan==0) break;
-			Bit8u scan_length=(read_buf[read_buf_pos]&7)+1;		// length of data struct
-			read_buf_pos+=2;
-			i+=3;
-			if (((scan&0x7f)<=MAX_SCAN_CODE) && (scan_length>0)) {
-				// add all available mappings
-				for (Bit16u addmap=0; addmap<scan_length; addmap++) {
-					if (addmap>additional_planes+2) break;
-					Bitu charptr=read_buf_pos+addmap*((read_buf[read_buf_pos-2]&0x80)?2:1);
-					Bit16u kchar=read_buf[charptr];
-
-					if (kchar!=0) {		// key remapped
-						if (read_buf[read_buf_pos-2]&0x80) kchar|=read_buf[charptr+1]<<8;	// scancode/char pair
-						// overwrite mapping
-						current_layout[scan*layout_pages+addmap]=kchar;
-						// clear command bit
-						current_layout[scan*layout_pages+layout_pages-2]&=~(1<<addmap);
-						// add command bit
-						current_layout[scan*layout_pages+layout_pages-2]|=(read_buf[read_buf_pos-1] & (1<<addmap));
-					}
-				}
+	return 0;
+};
 
-				current_layout[scan*layout_pages+layout_pages-1]=read_buf[read_buf_pos-2];	// flags
-				if (read_buf[read_buf_pos-2]&0x80) scan_length*=2;		// granularity flag (S)
-			}
-			i+=scan_length;		// advance pointer
-			read_buf_pos+=scan_length;
+KeyboardLayout::keyitem* KeyboardLayout::lookupKeyTable(submapping* s, unsigned int scan)
+{
+	uint16_t tableOff = HostReadUInt16(&s->tableOff);
+	int pos = tableOff;
+
+	if (!pos) return 0; // Empty table
+
+	//KEYB_DEBUG("Entering search at 0x%x", pos);
+
+	while (_data[pos]) {
+		//KEYB_DEBUG(" search at %d 0x%x 0x%x", pos, pos, _data[pos]);
+		keyitem* k = reinterpret_cast<keyitem*>(&_data[pos]);
+
+		//KEYB_DEBUG(" scancode 0x%x", k->scancode);
+
+		if (scan == k->scancode)
+			return k; // Found!
+
+		int skip = (k->flags & 0x7) + 1;
+
+		if (k->flags & 0x80) {
+			// Double column size
+			skip *= 2;
 		}
-		if (specific_layout==sub_map) break;
+
+		pos += skip + 3; // table header overhead
 	}
 
-	if (found_matching_layout) {
-		if (specific_layout==-1) LOG(LOG_BIOS,LOG_NORMAL)("Keyboard layout %s successfully loaded",keyboard_file_name);
-		else LOG(LOG_BIOS,LOG_NORMAL)("Keyboard layout %s (%i) successfully loaded",keyboard_file_name,specific_layout);
-		this->use_foreign_layout=true;
-		return KEYB_NOERROR;
+	return 0;
+};
+
+KeyboardLayout::diacritic* KeyboardLayout::lookupDiacritic
+(submapping* s, int di)
+{
+	uint16_t tableOff = HostReadUInt16(&s->diacriticOff);
+	int pos = tableOff;
+	int i = 0;
+
+	if (!pos) return 0; // Empty table
+
+	//KEYB_DEBUG("Entering search at 0x%x", pos);
+
+	while (_data[pos] && i <= di) {
+		diacritic* d = reinterpret_cast<diacritic*>(&_data[pos]);
+
+		//KEYB_DEBUG(" diacritic individual %c", d->individual);
+
+		if (i == di)
+			return d; // Found!
+
+		int skip = d->numPairs;
+		// Double column size
+		skip *= 2;
+
+		pos += skip + 2; // table header overhead
+		i++;
 	}
 
-	LOG(LOG_BIOS,LOG_ERROR)("No matching keyboard layout found in %s",keyboard_file_name);
+	return 0;
+};
 
-	// reset layout data (might have been changed by general layout)
-	this->reset();
+KeyboardLayout::diacritic* KeyboardLayout::lookupDiacritic(int di)
+{
+	keybcb* cb = getKeybCB();
 
-	return KEYB_LAYOUTNOTFOUND;
-}
+	// Try active submapping
+	if (cb->curSubmapping) {
+		submapping* s = getSubmapping(cb->curSubmapping);
+		diacritic* d = lookupDiacritic(s, di);
 
-bool keyboard_layout::layout_key(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3) {
-	if (key>MAX_SCAN_CODE) return false;
-	if (!this->use_foreign_layout) return false;
-
-	bool is_special_pair=(current_layout[key*layout_pages+layout_pages-1] & 0x80)==0x80;
-
-	if ((((flags1&used_lock_modifiers)&0x7c)==0) && ((flags3&2)==0)) {
-		// check if shift/caps is active:
-		// (left_shift OR right_shift) XOR (key_affected_by_caps AND caps_locked)
-		if ((((flags1&2)>>1) | (flags1&1)) ^ (((current_layout[key*layout_pages+layout_pages-1] & 0x40) & (flags1 & 0x40))>>6)) {
-			// shift plane
-			if (current_layout[key*layout_pages+1]!=0) {
-				// check if command-bit is set for shift plane
-				bool is_command=(current_layout[key*layout_pages+layout_pages-2]&2)!=0;
-				if (this->map_key(key, current_layout[key*layout_pages+1],
-					is_command, is_special_pair)) return true;
-			}
-		} else {
-			// normal plane
-			if (current_layout[key*layout_pages]!=0) {
-				// check if command-bit is set for normal plane
-				bool is_command=(current_layout[key*layout_pages+layout_pages-2]&1)!=0;
-				if (this->map_key(key, current_layout[key*layout_pages],
-					is_command, is_special_pair)) return true;
-			}
-		}
+		if (d)
+			return d;
 	}
 
-	// calculate current flags
-	Bit16u current_flags=(flags1&0x7f) | (((flags2&3) | (flags3&0xc))<<8);
-	if (flags1&3) current_flags|=0x4000;	// either shift key active
-	if (flags3&2) current_flags|=0x1000;	// e0 prefixed
-
-	// check all planes if flags fit
-	for (Bit16u cplane=0; cplane<additional_planes; cplane++) {
-		Bit16u req_flags=current_layout_planes[cplane].required_flags;
-		Bit16u req_userflags=current_layout_planes[cplane].required_userflags;
-		// test flags
-		if (((current_flags & req_flags)==req_flags) &&
-			((user_keys & req_userflags)==req_userflags) &&
-			((current_flags & current_layout_planes[cplane].forbidden_flags)==0) &&
-			((user_keys & current_layout_planes[cplane].forbidden_userflags)==0)) {
-				// remap key
-				if (current_layout[key*layout_pages+2+cplane]!=0) {
-					// check if command-bit is set for this plane
-					bool is_command=((current_layout[key*layout_pages+layout_pages-2]>>(cplane+2))&1)!=0;
-					if (this->map_key(key, current_layout[key*layout_pages+2+cplane],
-						is_command, is_special_pair)) return true;
-				} else break;	// abort plane checking
-			}
+	// Try normal submapping
+	submapping* s = getSubmapping(0);
+	diacritic* d = lookupDiacritic(s, di);
+
+	return d;
+};
+
+KeyboardLayout::kstring* KeyboardLayout::lookupString
+(submapping* s, int si)
+{
+	uint16_t tableOff = HostReadUInt16(&s->stringOff);
+	int pos = tableOff;
+	int i = 0;
+
+	if (!pos) return 0; // Empty table
+
+	while (_data[pos] && i <= si) {
+		kstring* k = reinterpret_cast<kstring*>(&_data[pos]);
+
+		if (i == si)
+			return k; // Found!
+
+		int skip = k->numPairs * 2;
+
+		pos += skip + 1; // table header overhead
+		i++;
 	}
 
-	if (diacritics_character>0) {
-		// ignore state-changing keys
-		switch(key) {
-			case 0x1d:			/* Ctrl Pressed */
-			case 0x2a:			/* Left Shift Pressed */
-			case 0x36:			/* Right Shift Pressed */
-			case 0x38:			/* Alt Pressed */
-			case 0x3a:			/* Caps Lock */
-			case 0x45:			/* Num Lock */
-			case 0x46:			/* Scroll Lock */
-				break;
-			default:
-				if (diacritics_character-200>=diacritics_entries) {
-					diacritics_character=0;
-					return true;
-				}
-				Bit16u diacritics_start=0;
-				// search start of subtable
-				for (Bit16u i=0; i<diacritics_character-200; i++)
-					diacritics_start+=diacritics[diacritics_start+1]*2+2;
+	return 0;
+};
 
-				BIOS_AddKeyToBuffer((Bit16u)(key<<8) | diacritics[diacritics_start]);
-				diacritics_character=0;
-		}
+KeyboardLayout::kstring* KeyboardLayout::lookupString(int si)
+{
+	keybcb* cb = getKeybCB();
+
+	// Try active submapping
+	if (cb->curSubmapping) {
+		submapping* s = getSubmapping(cb->curSubmapping);
+		kstring* k = lookupString(s, si);
+
+		if (k)
+			return k;
 	}
 
-	return false;
-}
+	// Try normal submapping
+	submapping* s = getSubmapping(0);
+	kstring* k = lookupString(s, si);
 
-bool keyboard_layout::map_key(Bitu key, Bit16u layouted_key, bool is_command, bool is_keypair) {
-	if (is_command) {
-		Bit8u key_command=(Bit8u)(layouted_key&0xff);
-		// check if diacritics-command
-		if ((key_command>=200) && (key_command<235)) {
-			// diacritics command
-			diacritics_character=key_command;
-			if (diacritics_character-200>=diacritics_entries) diacritics_character=0;
-			return true;
-		} else if ((key_command>=120) && (key_command<140)) {
-			// switch layout command
-			this->read_keyboard_file(key_command-119);
-			return true;
-		} else if ((key_command>=180) && (key_command<188)) {
-			// switch user key off
-			user_keys&=~(1<<(key_command-180));
-			return true;
-		} else if ((key_command>=188) && (key_command<196)) {
-			// switch user key on
-			user_keys|=(1<<(key_command-188));
-			return true;
-		} else if (key_command==160) return true;	// nop command
-	} else {
-		// non-command
-		if (diacritics_character>0) {
-			if (diacritics_character-200>=diacritics_entries) diacritics_character = 0;
-			else {
-				Bit16u diacritics_start=0;
-				// search start of subtable
-				for (Bit16u i=0; i<diacritics_character-200; i++)
-					diacritics_start+=diacritics[diacritics_start+1]*2+2;
-
-				Bit8u diacritics_length=diacritics[diacritics_start+1];
-				diacritics_start+=2;
-				diacritics_character=0;	// reset
-
-				// search scancode
-				for (Bit16u i=0; i<diacritics_length; i++) {
-					if (diacritics[diacritics_start+i*2]==(layouted_key&0xff)) {
-						// add diacritics to keybuf
-						BIOS_AddKeyToBuffer((Bit16u)(key<<8) | diacritics[diacritics_start+i*2+1]);
-						return true;
-					}
-				}
-				// add standard-diacritics to keybuf
-				BIOS_AddKeyToBuffer((Bit16u)(key<<8) | diacritics[diacritics_start-2]);
-			}
+	return k;
+};
+
+bool KeyboardLayout::doKey
+(KeyboardLayout::keyitem* k, int planeNum, uint16_t status)
+{
+	if (k->flags & (1 << 5)) {
+		// This key is affected by NumLock
+		if (status & 0x20) {
+			if (planeNum == 0)
+				planeNum = 1;
+			else if (planeNum == 1)
+				planeNum = 0;
 		}
+	}
 
-		// add remapped key to keybuf
-		if (is_keypair) BIOS_AddKeyToBuffer(layouted_key);
-		else BIOS_AddKeyToBuffer((Bit16u)(key<<8) | (layouted_key&0xff));
+	if (k->flags & (1 << 6)) {
+		// This key is affected by CapsLock
+		if (status & 0x40) {
+			if (planeNum == 0)
+				planeNum = 1;
+			else if (planeNum == 1)
+				planeNum = 0;
+		}
+	}
+
+	int numColumns = (k->flags & 0x7) + 1;
+	if (planeNum > numColumns) {
+		KEYB_DEBUG("Too few planes for this key");
+		return false;
+	}
+
+	bool isKeyPair = k->flags & (1 << 7);
+	bool isCommand = k->command & (1 << planeNum);
+	uint16_t data;
+
+	if (isKeyPair) {
+		data = HostReadUInt16(&k->words[planeNum]);
+	} else {
+		data = (k->scancode << 8) | (k->bytes[planeNum] & 0xFF);
+	}
 
+	if (isCommand) {
+		return doCommand(data & 0xFF);
+	} else {
+		sendKey(data);
 		return true;
 	}
+};
+
+bool KeyboardLayout::doDiacritic(diacritic* d, uint16_t data)
+{
+	char character = data & 0xFF;
+	data = data & 0xFF00; // Clear character; leave keycode
+
+	for (int i = 0; i < d->numPairs; i++) {
+		if (character == d->pairs[i].test) {
+			sendKey(data | (d->pairs[i].produce & 0xFF), false);
+			return true; // handled
+		}
+	}
+
+	// Not found: display "Diacritic individal character"
+	KEYB_DEBUG("Sending diacritic individual character 0x%x (%c)",
+		d->individual & 0xFF, d->individual);
+	sendKey(data | (d->individual & 0xFF), false);
 	return false;
 }
 
-Bit16u keyboard_layout::extract_codepage(const char* keyboard_file_name) {
-	if (!strcmp(keyboard_file_name,"none")) return 437;
-
-	Bit32u read_buf_size;
-	static Bit8u read_buf[65535];
-	Bit32u start_pos=5;
+bool KeyboardLayout::doCommand(unsigned int cmd)
+{
+	KEYB_DEBUG("%s %u", __func__, cmd);
+	if (cmd == 0) {
+		return false;						// IGNORE
+	} else if (cmd >= 1 && cmd <= 79) {		// STRINGS
+		cmd -= 1;
+		return doString(cmd);
+	} else if (cmd >= 80 && cmd <= 89) {
+	} else if (cmd >= 90 && cmd <= 99) {
+	} else if (cmd == 100) {				// DISABLE
+		// XXX This is a hack (does the equivalent of @code{delete this})
+		DOS_LoadKeyboardLayout("none", "none", "none", "none");
+		return true;
+	} else if (cmd == 101) {
+	} else if (cmd >= 102 && cmd <= 119) {
+	} else if (cmd >= 120 && cmd <= 139) {
+	} else if (cmd >= 140 && cmd <= 143) {
+	} else if (cmd >= 150 && cmd <= 154) {
+	} else if (cmd == 160) {  				// NO OPERATION
+		return true;
+	} else if (cmd == 161) {
+	} else if (cmd == 162) {
+	} else if (cmd == 163) {
+	} else if (cmd == 164) {
+	} else if (cmd >= 168 && cmd <= 179) {
+	} else if (cmd >= 180 && cmd <= 187) {	// USER DEFINED KEYS OFF
+		cmd -= 180;
+		KEYB_DEBUG("User key %u off", cmd);
+		_userStatus &= ~(1 << cmd);
+		return true;
+	} else if (cmd >= 188 && cmd <= 195) {	// USER DEFINED KEYS ON
+		cmd -= 188;
+		KEYB_DEBUG("User key %u on", cmd);
+		_userStatus |= 1 << cmd;
+		return true;
+	} else if (cmd >= 200 && cmd <= 234) {	// DIACRITIC SEQUENCE STARTERS
+		cmd -= 200;
+		_curDiacritic = lookupDiacritic(cmd);
 
-	char nbuf[512];
-	sprintf(nbuf, "%s.kl", keyboard_file_name);
-	FILE* tempfile = OpenDosboxFile(nbuf);
-	if (tempfile==NULL) {
-		// try keyboard layout libraries next
-		if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,true))) {
-			tempfile = OpenDosboxFile("keyboard.sys");
-		} else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,true))) {
-			tempfile = OpenDosboxFile("keybrd2.sys");
-		} else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,true))) {
-			tempfile = OpenDosboxFile("keybrd3.sys");
-		} else if ((start_pos=read_kcl_file("keyboard.sys",keyboard_file_name,false))) {
-			tempfile = OpenDosboxFile("keyboard.sys");
-		} else if ((start_pos=read_kcl_file("keybrd2.sys",keyboard_file_name,false))) {
-			tempfile = OpenDosboxFile("keybrd2.sys");
-		} else if ((start_pos=read_kcl_file("keybrd3.sys",keyboard_file_name,false))) {
-			tempfile = OpenDosboxFile("keybrd3.sys");
-		} else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,true))) {
-			read_buf_size=0;
-			for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
-		} else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,true))) {
-			read_buf_size=0;
-			for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
-		} else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,true))) {
-			read_buf_size=0;
-			for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
-		} else if ((start_pos=read_kcl_data(layout_keyboardsys,33196,keyboard_file_name,false))) {
-			read_buf_size=0;
-			for (Bitu ct=start_pos+2; ct<33196; ct++) read_buf[read_buf_size++]=layout_keyboardsys[ct];
-		} else if ((start_pos=read_kcl_data(layout_keybrd2sys,25431,keyboard_file_name,false))) {
-			read_buf_size=0;
-			for (Bitu ct=start_pos+2; ct<25431; ct++) read_buf[read_buf_size++]=layout_keybrd2sys[ct];
-		} else if ((start_pos=read_kcl_data(layout_keybrd3sys,27122,keyboard_file_name,false))) {
-			read_buf_size=0;
-			for (Bitu ct=start_pos+2; ct<27122; ct++) read_buf[read_buf_size++]=layout_keybrd3sys[ct];
-		} else {
-			start_pos=0;
-			LOG(LOG_BIOS,LOG_ERROR)("Keyboard layout file %s not found",keyboard_file_name);
-			return 437;
-		}
-		if (tempfile) {
-			fseek(tempfile, start_pos+2, SEEK_SET);
-			read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
-			fclose(tempfile);
-		}
-		start_pos=0;
-	} else {
-		// check ID-bytes of file
-		Bit32u dr=(Bit32u)fread(read_buf, sizeof(Bit8u), 4, tempfile);
-		if ((dr<4) || (read_buf[0]!=0x4b) || (read_buf[1]!=0x4c) || (read_buf[2]!=0x46)) {
-			LOG(LOG_BIOS,LOG_ERROR)("Invalid keyboard layout file %s",keyboard_file_name);
-			return 437;
+		if (!_curDiacritic) {
+			KEYB_WARN("Diacritic %u not found", cmd);
+			return false;
 		}
 
-		fseek(tempfile, 0, SEEK_SET);
-		read_buf_size=(Bit32u)fread(read_buf, sizeof(Bit8u), 65535, tempfile);
-		fclose(tempfile);
+		KEYB_DEBUG("Diacritic %u enabled", cmd);
+		return true;
+	} else {
+		KEYB_WARN("Unknown command %d", cmd);
+		return false;
 	}
 
-	Bit8u data_len,submappings;
-	data_len=read_buf[start_pos++];
+	return false;
+};
+
+bool KeyboardLayout::doString(int si)
+{
+	kstring* k = lookupString(si);
 
-	start_pos+=data_len;		// start_pos==absolute position of KeybCB block
+	if (!k) {
+		KEYB_WARN("String %u not found", si);
+		return false;
+	}
 
-	submappings=read_buf[start_pos];
+	for (int i = 0; i < k->numPairs; i++) {
+		sendKey(HostReadUInt16(&k->pairs[i]));
+	}
 
-	// check all submappings and use them if general submapping or same codepage submapping
-	for (Bit16u sub_map=0; (sub_map<submappings); sub_map++) {
-		Bit16u submap_cp;
+	return true;
+};
 
-		// read codepage of submapping
-		submap_cp=host_readw(&read_buf[start_pos+0x14+sub_map*8]);
+static KeyboardLayout* layout = 0;
 
-		if (submap_cp!=0) return submap_cp;
-	}
-	return 437;
-}
+/* CODEPAGE LOADING STUFF */
+/* I wasn't expected to understand this. I didn't. */
 
-Bitu keyboard_layout::read_codepage_file(const char* codepage_file_name, Bit32s codepage_id) {
+static err_t loadCodepage(const char* codepage_file_name, Bit32s codepage_id)
+{
 	char cp_filename[512];
 	strcpy(cp_filename, codepage_file_name);
-	if (!strcmp(cp_filename,"none")) return KEYB_NOERROR;
 
 	if (codepage_id==dos.loaded_codepage) return KEYB_NOERROR;
 
-	if (!strcmp(cp_filename,"auto")) {
+	if (strcasecmp(cp_filename, "auto") == 0) {
 		// select matching .cpi-file for specified codepage
 		switch (codepage_id) {
 			case 437:	case 850:	case 852:	case 853:	case 857:	case 858:	
@@ -722,7 +955,7 @@
 			case 667:	case 668:	case 790:	case 867:	case 991:	case 57781:
 						sprintf(cp_filename, "EGA10.CPI"); break;
 			default:
-				LOG_MSG("No matching cpi file for codepage %i",codepage_id);
+				KEYB_WARN("No matching cpi file for codepage %i",codepage_id);
 				return KEYB_INVALIDCPFILE;
 		}
 	}
@@ -731,20 +964,21 @@
 	Bit16u number_of_codepages;
 
 	char nbuf[512];
+	char hostPath[CROSS_LEN];
 	sprintf(nbuf, "%s", cp_filename);
-	FILE* tempfile=OpenDosboxFile(nbuf);
-	if (tempfile==NULL) {
+	bool found = searchDosFile(nbuf, hostPath);
+	if (!found) {
 		size_t strsz=strlen(nbuf);
 		if (strsz) {
 			char plc=(char)toupper(*reinterpret_cast<unsigned char*>(&nbuf[strsz-1]));
 			if (plc=='I') {
 				// try CPX-extension as well
 				nbuf[strsz-1]='X';
-				tempfile=OpenDosboxFile(nbuf);
+				found = searchDosFile(nbuf, hostPath);
 			} else if (plc=='X') {
 				// try CPI-extension as well
 				nbuf[strsz-1]='I';
-				tempfile=OpenDosboxFile(nbuf);
+				found = searchDosFile(nbuf, hostPath);
 			}
 		}
 	}
@@ -753,7 +987,8 @@
 	Bit32u cpi_buf_size=0,size_of_cpxdata=0;;
 	bool upxfound=false;
 	Bit16u found_at_pos=5;
-	if (tempfile==NULL) {
+	if (!found) {
+		KEYB_DEBUG("Using builtin codepage file");
 		// check if build-in codepage is available
 		switch (codepage_id) {
 			case 437:	case 850:	case 852:	case 853:	case 857:	case 858:	
@@ -769,6 +1004,7 @@
 						cpi_buf_size=5720;
 						break;
 			default: 
+				KEYB_WARN("Couldn't find builtin codepage %i", codepage_id);
 				return KEYB_INVALIDCPFILE;
 				break;
 		}
@@ -776,10 +1012,13 @@
 		found_at_pos=0x29;
 		size_of_cpxdata=cpi_buf_size;
 	} else {
+		KEYB_DEBUG("Using host codepage file %s", hostPath);
+		FILE * tempfile = fopen(hostPath, "rb");
+
 		Bit32u dr=(Bit32u)fread(cpi_buf, sizeof(Bit8u), 5, tempfile);
 		// check if file is valid
 		if (dr<5) {
-			LOG(LOG_BIOS,LOG_ERROR)("Codepage file %s invalid",cp_filename);
+			KEYB_WARN("Codepage file %s invalid",cp_filename);
 			return KEYB_INVALIDCPFILE;
 		}
 		// check if non-compressed cpi file
@@ -788,7 +1027,7 @@
 			// check if dr-dos custom cpi file
 			if ((cpi_buf[0]==0x7f) && (cpi_buf[1]!=0x44) && (cpi_buf[2]!=0x52) && 
 				(cpi_buf[3]!=0x46) && (cpi_buf[4]!=0x5f)) {
-				LOG(LOG_BIOS,LOG_ERROR)("Codepage file %s has unsupported DR-DOS format",cp_filename);
+				KEYB_WARN("Codepage file %s has unsupported DR-DOS format",cp_filename);
 				return KEYB_INVALIDCPFILE;
 			}
 			// check if compressed cpi file
@@ -814,7 +1053,7 @@
 				if (upxfound) break;
 			}
 			if (!upxfound) {
-				LOG(LOG_BIOS,LOG_ERROR)("Codepage file %s invalid: %x",cp_filename,cpi_buf[0]);
+				KEYB_WARN("Codepage file %s invalid: %x",cp_filename,cpi_buf[0]);
 				return KEYB_INVALIDCPFILE;
 			} else {
 				if (next_byte<10) E_Exit("UPX-compressed cpi file, but upx-version too old");
@@ -928,14 +1167,14 @@
 				font_data_start+=font_height*256;
 			}
 
-			LOG(LOG_BIOS,LOG_NORMAL)("Codepage %i successfully loaded",codepage_id);
+			KEYB_DEBUG("Codepage %i successfully loaded",codepage_id);
 
 			// set codepage entries
 			dos.loaded_codepage=(Bit16u)(codepage_id&0xffff);
 
 			// update font if necessary
-			if (font_changed && (CurMode->type==M_TEXT) && (IS_EGAVGA_ARCH)) {
-				INT10_ReloadFont();
+			if (font_changed && (CurMode->type==M_TEXT) && (machine==MCH_VGA)) {
+				INT10_LoadFont(Real2Phys(int10.rom.font_16),true,256,0,0,16);
 			}
 			INT10_SetupRomMemoryChecksum();
 
@@ -946,347 +1185,248 @@
 		start_pos+=2;
 	}
 
-	LOG(LOG_BIOS,LOG_ERROR)("Codepage %i not found",codepage_id);
-
+	KEYB_WARN("Codepage %i not found",codepage_id);
 	return KEYB_INVALIDCPFILE;
+};
+
+static void unloadCodepage()
+{
+	if ((dos.loaded_codepage!=437) && (CurMode->type==M_TEXT)) {
+		INT10_ReloadRomFonts();
+		dos.loaded_codepage=437;	// US codepage
+	}
+};
+
+// Called by int9-handler
+bool DOS_LayoutKey(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3) {
+	KEYB_DEBUG("key %lx %s %x %x %x", key & 0x7F, key & 0x80 ? "break\t" : "\t\t",
+					flags1, flags2, flags3);
+
+	if (!layout) return false; // No layout loaded
+
+	return layout->handleKey(key, flags1, flags2, flags3);
 }
 
-Bitu keyboard_layout::switch_keyboard_layout(const char* new_layout, keyboard_layout*& created_layout, Bit32s& tried_cp) {
-	if (strncasecmp(new_layout,"US",2)) {
-		// switch to a foreign layout
-		char tbuf[256];
-		strcpy(tbuf, new_layout);
-		size_t newlen=strlen(tbuf);
-
-		bool language_code_found=false;
-		// check if language code is present in loaded foreign layout
-		for (Bitu i=0; i<language_code_count; i++) {
-			if (!strncasecmp(tbuf,language_codes[i],newlen)) {
-				language_code_found=true;
-				break;
-			}
-		}
+static bool searchDosFile(const char* name, char* fullName) {
+	uint8_t drive;
 
-		if (language_code_found) {
-			if (!this->use_foreign_layout) {
-				// switch to foreign layout
-				this->use_foreign_layout=true;
-				diacritics_character=0;
-				LOG(LOG_BIOS,LOG_NORMAL)("Switched to layout %s",tbuf);
-			}
+	localDrive* ldp=0;
+	char dosName[DOS_PATHLENGTH];
+	// try to build dos name
+	if (DOS_MakeName(name, dosName, &drive)) {
+		// try to open file on mounted drive first
+		ldp=dynamic_cast<localDrive*>(Drives[drive]);
+
+		if (!ldp) // Drive is not local
+			return false;
+
+		FILE *tmpfile = ldp->GetSystemFilePtr(dosName, "rb");
+		if (tmpfile) {
+			fclose(tmpfile);
+			return ldp->GetSystemFilename(fullName, dosName);
 		} else {
-			keyboard_layout * temp_layout=new keyboard_layout();
-			Bitu req_codepage=temp_layout->extract_codepage(new_layout);
-			tried_cp = req_codepage;
-			Bitu kerrcode=temp_layout->read_keyboard_file(new_layout, req_codepage);
-			if (kerrcode) {
-				delete temp_layout;
-				return kerrcode;
-			}
-			// ...else keyboard layout loaded successfully, change codepage accordingly
-			kerrcode=temp_layout->read_codepage_file("auto", req_codepage);
-			if (kerrcode) {
-				delete temp_layout;
-				return kerrcode;
-			}
-			// Everything went fine, switch to new layout
-			created_layout=temp_layout;
+			return false;
 		}
-	} else if (this->use_foreign_layout) {
-		// switch to the US layout
-		this->use_foreign_layout=false;
-		diacritics_character=0;
-		LOG(LOG_BIOS,LOG_NORMAL)("Switched to US layout");
 	}
-	return KEYB_NOERROR;
-}
 
-void keyboard_layout::switch_foreign_layout() {
-	this->use_foreign_layout=!this->use_foreign_layout;
-	diacritics_character=0;
-	if (this->use_foreign_layout) LOG(LOG_BIOS,LOG_NORMAL)("Switched to foreign layout");
-	else LOG(LOG_BIOS,LOG_NORMAL)("Switched to US layout");
+	return false;
 }
 
-const char* keyboard_layout::get_layout_name() {
-	// get layout name (language ID or NULL if default layout)
-	if (use_foreign_layout) {
-		if (strcmp(current_keyboard_file_name,"none") != 0) {
-			return (const char*)&current_keyboard_file_name;
-		}
+static KeyboardLayout* autoSearchLayoutFile(const char * layoutName) {
+	KeyboardLayout* newLayout = 0;
+	char hostFileName[CROSS_LEN];
+
+	KEYB_DEBUG("%s: %s", __func__, layoutName);
+
+	// Try external files
+	if (searchDosFile("keyboard.sys", hostFileName)) {
+		newLayout = new KeyboardLayout();
+		if (newLayout->load(hostFileName, layoutName) == KEYB_NOERROR)
+			return newLayout;
+	}
+	if (searchDosFile("keybrd2.sys", hostFileName)) {
+		newLayout = new KeyboardLayout();
+		if (newLayout->load(hostFileName, layoutName) == KEYB_NOERROR)
+			return newLayout;
+	}
+	if (searchDosFile("keybrd3.sys", hostFileName)) {
+		newLayout = new KeyboardLayout();
+		if (newLayout->load(hostFileName, layoutName) == KEYB_NOERROR)
+			return newLayout;
+	}
+
+	// Try builtin files
+	{
+		aistream ais(layout_keyboardsys, sizeof(layout_keyboardsys));
+		newLayout = new KeyboardLayout();
+		if (newLayout->load("builtin:\\keyboard.sys", ais, layoutName)
+			== KEYB_NOERROR)
+			return newLayout;
+	}
+	{
+		aistream ais(layout_keybrd2sys, sizeof(layout_keybrd2sys));
+		newLayout = new KeyboardLayout();
+		if (newLayout->load("builtin:\\keybrd2.sys", ais, layoutName)
+			== KEYB_NOERROR)
+			return newLayout;
+	}
+	{
+		aistream ais(layout_keybrd3sys, sizeof(layout_keybrd3sys));
+		newLayout = new KeyboardLayout();
+		if (newLayout->load("builtin:\\keybrd3.sys", ais, layoutName)
+			== KEYB_NOERROR)
+			return newLayout;
 	}
-	return NULL;
-}
 
-const char* keyboard_layout::main_language_code() {
-	if (language_codes) {
-		return language_codes[0];
-	}
-	return NULL;
+	return 0;
 }
 
+Bitu DOS_LoadKeyboardLayout(const char * layoutFile, const char * layoutName,
+						const char * codepageFile, const char * codepageName)
+{
+	KEYB_DEBUG("%s: layout: %s (%s), codepage: %s (%s)", __func__,
+		layoutFile, layoutName, codepageFile, codepageName);
+
+	err_t err = KEYB_NOERROR;
+	char hostFileName[CROSS_LEN];
+	KeyboardLayout* newLayout = 0;
+
+	bool haveLayoutFile = (strcasecmp(layoutFile, "none") != 0);
+	bool haveLayoutName = (strcasecmp(layoutName, "none") != 0);
+	bool haveCodepageFile = (strcasecmp(codepageFile, "none") != 0);
+	bool haveCodepageName = (strcasecmp(codepageName, "none") != 0);
+
+	// Handle layout
+	if (haveLayoutFile && haveLayoutName) {
+		// Both layoutFile and layoutName are not set to "none"
+		// This means we have to load something
+
+		bool autoLayoutFile = (strcasecmp(layoutFile, "auto") == 0);
+		bool autoLayoutName = (strcasecmp(layoutName, "auto") == 0);
+
+		if (autoLayoutFile && autoLayoutName) {
+			// Both layoutName and layoutFile are set to "auto"
+			// Set a proper layoutName based on system locale
+			// Leave layoutFile as "auto"
+			layoutName = "us"; // TODO
+			autoLayoutName = false;
+			KEYB_DEBUG("Autodetected layout: %s", layoutName);
+		}
+
+		if (autoLayoutFile) {
+			// layoutFile is "auto" but layoutName is specified
+			// Search builtin and key*.sys files
+			newLayout = autoSearchLayoutFile(layoutName);
+
+			if (!newLayout)
+				err = KEYB_LAYOUTNOTFOUND;
+
+		} else if (searchDosFile(layoutFile, hostFileName)) {
+			// Both layoutFile and layoutName is specified
+			// Try to parse layoutFile as DOS file path
+			KEYB_DEBUG("DOS layout file name: %s (file on host: %s)",
+				layoutFile, hostFileName);
+
+			newLayout = new KeyboardLayout();
+			if (autoLayoutName)
+				err = newLayout->load(hostFileName);
+			else
+				err = newLayout->load(hostFileName, layoutName);
 
-static keyboard_layout* loaded_layout=NULL;
+		} else {
+			// Both layoutFile and layoutName is specified
+			// Try to parse layoutFile as host file path
+			KEYB_DEBUG("Host layout file name: %s", layoutFile);
+
+			newLayout = new KeyboardLayout();
+			if (autoLayoutName)
+				err = newLayout->load(layoutFile);
+			else
+				err = newLayout->load(layoutFile, layoutName);
+		}
+
+		if (err)
+			goto err_handle;
+	}
+
+	// Handle codepage
+	if (haveCodepageFile && haveCodepageName) {
+	 	int codepageNum;
+	 	bool autoCodepageName = (strcasecmp(codepageName, "auto") == 0);
+
+	 	if (autoCodepageName) {
+	 		if (newLayout) {
+	 			codepageNum = newLayout->getCodepage();
+	 			KEYB_DEBUG("Autoselecting codepage %d from layout default",
+	 				codepageNum);
+	 		} else {
+	 			codepageNum = -1;
+	 			KEYB_DEBUG("Layout has no default codepage");
+	 		}
+	 	} else {
+	 		codepageNum = atoi(codepageName);
+	 	}
+
+	 	if (codepageNum > 0) {
+	 		KEYB_DEBUG("Loading codepage %d...", codepageNum);
+		 	err = loadCodepage(codepageFile, codepageNum);
+
+		 	if (err) {
+				goto err_handle;
+			} else if (newLayout) {
+				newLayout->selectCodepage(codepageNum);
+			}
+		} else {
+			unloadCodepage();
+		}
+	} else {
+		unloadCodepage();
+	}
 
-// CTRL-ALT-F2 switches between foreign and US-layout using this function
-/* static void switch_keyboard_layout(bool pressed) {
-	if (!pressed)
-		return;
-	if (loaded_layout) loaded_layout->switch_foreign_layout();
-} */
+	if (layout) delete layout;
+	layout = newLayout;
 
-// called by int9-handler
-bool DOS_LayoutKey(Bitu key, Bit8u flags1, Bit8u flags2, Bit8u flags3) {
-	if (loaded_layout) return loaded_layout->layout_key(key, flags1, flags2, flags3);
-	else return false;
-}
+	KEYB_DEBUG("Sucess");
 
-Bitu DOS_LoadKeyboardLayout(const char * layoutname, Bit32s codepage, const char * codepagefile) {
-	keyboard_layout * temp_layout=new keyboard_layout();
-	// try to read the layout for the specified codepage
-	Bitu kerrcode=temp_layout->read_keyboard_file(layoutname, codepage);
-	if (kerrcode) {
-		delete temp_layout;
-		return kerrcode;
-	}
-	// ...else keyboard layout loaded successfully, change codepage accordingly
-	kerrcode=temp_layout->read_codepage_file(codepagefile, codepage);
-	if (kerrcode) {
-		delete temp_layout;
-		return kerrcode;
-	}
-	// Everything went fine, switch to new layout
-	loaded_layout=temp_layout;
 	return KEYB_NOERROR;
-}
 
-Bitu DOS_SwitchKeyboardLayout(const char* new_layout, Bit32s& tried_cp) {
-	if (loaded_layout) {
-		keyboard_layout* changed_layout=NULL;
-		Bitu ret_code=loaded_layout->switch_keyboard_layout(new_layout, changed_layout, tried_cp);
-		if (changed_layout) {
-			// Remove old layout, activate new layout
-			delete loaded_layout;
-			loaded_layout=changed_layout;
-		}
-		return ret_code;
-	} else return 0xff;
+err_handle:
+	if (newLayout)
+		delete newLayout;
+
+	return err;
 }
 
-// get currently loaded layout name (NULL if no layout is loaded)
-const char* DOS_GetLoadedLayout(void) {
-	if (loaded_layout) {
-		return loaded_layout->get_layout_name();
+const char* DOS_GetLoadedLayout(void)
+{
+	if (layout) {
+		return layout->getLoadedLayout().c_str();
+	} else {
+		return 0;
 	}
-	return NULL;
 }
 
+void DOS_KeyboardLayout_ShutDown(Section* /*sec*/) {
+	DOS_LoadKeyboardLayout("none", "none", "none", "none");
+}
 
-class DOS_KeyboardLayout: public Module_base {
-public:
-	DOS_KeyboardLayout(Section* configuration):Module_base(configuration){
-		Section_prop * section=static_cast<Section_prop *>(configuration);
-		dos.loaded_codepage=437;	// US codepage already initialized
-		loaded_layout=new keyboard_layout();
-
-		const char * layoutname=section->Get_string("keyboardlayout");
-
-		Bits wants_dos_codepage = -1;
-		if (!strncmp(layoutname,"auto",4)) {
-#if defined (WIN32)
-			WORD cur_kb_layout = LOWORD(GetKeyboardLayout(0));
-			WORD cur_kb_subID  = 0;
-			char layoutID_string[KL_NAMELENGTH];
-			if (GetKeyboardLayoutName(layoutID_string)) {
-				if (strlen(layoutID_string) == 8) {
-					int cur_kb_layout_by_name = ConvHexWord((char*)&layoutID_string[4]);
-					layoutID_string[4] = 0;
-					int subID = ConvHexWord((char*)&layoutID_string[0]);
-					if ((cur_kb_layout_by_name>0) && (cur_kb_layout_by_name<65536)) {
-						// use layout ID extracted from the layout string
-						cur_kb_layout = (WORD)cur_kb_layout_by_name;
-					}
-					if ((subID>=0) && (subID<100)) {
-						// use sublanguage ID extracted from the layout string
-						cur_kb_subID  = (WORD)subID;
-					}
-				}
-			}
-			// try to match emulated keyboard layout with host-keyboardlayout
-			// codepage 437 (standard) is preferred
-			switch (cur_kb_layout) {
-/*				case 1026:
-					layoutname = "bg241";
-					break; */
-				case 1029:
-					layoutname = "cz243";
-					break;
-				case 1030:
-					layoutname = "dk";
-					break;
-				case 1031:
-					layoutname = "gr";
-					wants_dos_codepage = 437;
-					break;
-				case 1033:
-					// US
-					return;
-				case 1032:
-					layoutname = "gk";
-					break;
-				case 1034:
-					layoutname = "sp";
-					wants_dos_codepage = 437;
-					break;
-				case 1035:
-					layoutname = "su";
-					wants_dos_codepage = 437;
-					break;
-				case 1036:
-					layoutname = "fr";
-					wants_dos_codepage = 437;
-					break;
-				case 1038:
-					if (cur_kb_subID==1) layoutname = "hu";
-					else layoutname = "hu208";
-					break;
-				case 1039:
-					layoutname = "is161";
-					break;
-				case 1040:
-					layoutname = "it";
-					wants_dos_codepage = 437;
-					break;
-				case 1043:
-					layoutname = "nl";
-					wants_dos_codepage = 437;
-					break;
-				case 1044:
-					layoutname = "no";
-					break;
-				case 1045:
-					layoutname = "pl";
-					break;
-				case 1046:
-					layoutname = "br";
-					wants_dos_codepage = 437;
-					break;
-/*				case 1048:
-					layoutname = "ro446";
-					break; */
-				case 1049:
-					layoutname = "ru";
-					wants_dos_codepage = 437;
-					break;
-				case 1050:
-					layoutname = "hr";
-					break;
-				case 1051:
-					layoutname = "sk";
-					break;
-/*				case 1052:
-					layoutname = "sq448";
-					break; */
-				case 1053:
-					layoutname = "sv";
-					wants_dos_codepage = 437;
-					break;
-				case 1055:
-					layoutname = "tr";
-					break;
-				case 1058:
-					layoutname = "ur";
-					wants_dos_codepage = 437;
-					break;
-				case 1059:
-					layoutname = "bl";
-					break;
-				case 1060:
-					layoutname = "si";
-					break;
-				case 1061:
-					layoutname = "et";
-					break;
-/*				case 1062:
-					layoutname = "lv";
-					break; */
-/*				case 1063:
-					layoutname = "lt221";
-					break; */
-/*				case 1064:
-					layoutname = "tj";
-					break;
-				case 1066:
-					layoutname = "vi";
-					break;
-				case 1067:
-					layoutname = "hy";
-					break; */
-				case 2055:
-					layoutname = "sg";
-					wants_dos_codepage = 437;
-					break;
-				case 2070:
-					layoutname = "po";
-					break;
-				case 4108:
-					layoutname = "sf";
-					wants_dos_codepage = 437;
-					break;
-				default:
-					break;
-			}
-#endif
-		}
-
-		bool extract_codepage = true;
-		if (wants_dos_codepage>0) {
-			if ((loaded_layout->read_codepage_file("auto", (Bitu)wants_dos_codepage)) == KEYB_NOERROR) {
-				// preselected codepage was successfully loaded
-				extract_codepage = false;
-			}
-		}
-		if (extract_codepage) {
-			// try to find a good codepage for the requested layout
-			Bitu req_codepage = loaded_layout->extract_codepage(layoutname);
-			loaded_layout->read_codepage_file("auto", req_codepage);
-		}
-
-/*		if (strncmp(layoutname,"auto",4) && strncmp(layoutname,"none",4)) {
-			LOG_MSG("Loading DOS keyboard layout %s ...",layoutname);
-		} */
-		if (loaded_layout->read_keyboard_file(layoutname, dos.loaded_codepage)) {
-			if (strncmp(layoutname,"auto",4)) {
-				LOG_MSG("Error loading keyboard layout %s",layoutname);
-			}
-		} else {
-			const char* lcode = loaded_layout->main_language_code();
-			if (lcode) {
-				LOG_MSG("DOS keyboard layout loaded with main language code %s for layout %s",lcode,layoutname);
-			}
-		}
-	}
+void DOS_KeyboardLayout_Init(Section* sec) {
+	Section_prop * section=static_cast<Section_prop *>(sec);
+	dos.loaded_codepage=437;	// US codepage already initialized
 
-	~DOS_KeyboardLayout(){
-		if ((dos.loaded_codepage!=437) && (CurMode->type==M_TEXT)) {
-			INT10_ReloadRomFonts();
-			dos.loaded_codepage=437;	// US codepage
-		}
-		if (loaded_layout) {
-			delete loaded_layout;
-			loaded_layout=NULL;
-		}
-	}
-};
+	// Parse the keyboardlayout config setting; load whatever is appropiate
+	const char * layoutFile = section->Get_string("keyboardlayoutfile");
+	if (strlen(layoutFile) == 0) layoutFile = "auto";
+	const char * layoutName = section->Get_string("keyboardlayout");
+	if (strlen(layoutName) == 0) layoutName = "auto";
+	const char * codepageFile = section->Get_string("codepagefile");
+	if (strlen(codepageFile) == 0) codepageFile = "auto";
+	const char * codepageName = section->Get_string("codepage");
+	if (strlen(codepageName) == 0) codepageName = "auto";
 
-static DOS_KeyboardLayout* test;
+	DOS_LoadKeyboardLayout(layoutFile, layoutName,
+							codepageFile, codepageName);
 
-void DOS_KeyboardLayout_ShutDown(Section* /*sec*/) {
-	delete test;	
+	sec->AddDestroyFunction(&DOS_KeyboardLayout_ShutDown, true);
 }
 
-void DOS_KeyboardLayout_Init(Section* sec) {
-	test = new DOS_KeyboardLayout(sec);
-	sec->AddDestroyFunction(&DOS_KeyboardLayout_ShutDown,true);
-//	MAPPER_AddHandler(switch_keyboard_layout,MK_f2,MMOD1|MMOD2,"sw_layout","Switch Layout");
-}
Index: dosbox-0.74/src/dos/dos_programs.cpp
===================================================================
--- dosbox-0.74.orig/src/dos/dos_programs.cpp	2010-05-10 19:43:54.000000000 +0200
+++ dosbox-0.74/src/dos/dos_programs.cpp	2010-10-19 00:46:58.000000000 +0200
@@ -1270,8 +1270,8 @@
 }
 
 
-Bitu DOS_SwitchKeyboardLayout(const char* new_layout, Bit32s& tried_cp);
-Bitu DOS_LoadKeyboardLayout(const char * layoutname, Bit32s codepage, const char * codepagefile);
+Bitu DOS_LoadKeyboardLayout(const char * layoutFile, const char * layoutName,
+						const char * codepage, const char * codepageFileName);
 const char* DOS_GetLoadedLayout(void);
 
 class KEYB : public Program {
@@ -1281,46 +1281,57 @@
 
 void KEYB::Run(void) {
 	if (cmd->FindCommand(1,temp_line)) {
-		if (cmd->FindString("?",temp_line,false)) {
+		std::string layoutFile = temp_line;
+		std::string layoutName;
+		std::string codepageFile;
+		std::string codepageNum;
+
+		if (layoutFile.find('?') != std::string::npos) {
 			WriteOut(MSG_Get("PROGRAM_KEYB_SHOWHELP"));
 		} else {
-			/* first parameter is layout ID */
-			Bitu keyb_error=0;
-			std::string cp_string;
-			Bit32s tried_cp = -1;
-			if (cmd->FindCommand(2,cp_string)) {
-				/* second parameter is codepage number */
-				tried_cp=atoi(cp_string.c_str());
-				char cp_file_name[256];
-				if (cmd->FindCommand(3,cp_string)) {
-					/* third parameter is codepage file */
-					strcpy(cp_file_name, cp_string.c_str());
-				} else {
-					/* no codepage file specified, use automatic selection */
-					strcpy(cp_file_name, "auto");
-				}
-
-				keyb_error=DOS_LoadKeyboardLayout(temp_line.c_str(), tried_cp, cp_file_name);
-			} else {
-				keyb_error=DOS_SwitchKeyboardLayout(temp_line.c_str(), tried_cp);
+			/* first parameter is layout filename */
+
+			/* second parameter is layout name */
+			if (!cmd->FindCommand(2, layoutName))
+			{
+				layoutName = "auto";
+			}
+
+			/* third parameter is codepage file name */
+			if (!cmd->FindCommand(3, codepageFile))
+			{
+				codepageFile = "auto";
+			}
+
+			/* fourth parameter is codepage file name */
+			if (!cmd->FindCommand(4, codepageNum))
+			{
+				codepageNum = "auto";
 			}
+
+			Bitu keyb_error;
+
+			keyb_error = DOS_LoadKeyboardLayout(
+				layoutFile.c_str(), layoutName.c_str(),
+				codepageFile.c_str(), codepageNum.c_str()
+				);
+
 			switch (keyb_error) {
 				case KEYB_NOERROR:
-					WriteOut(MSG_Get("PROGRAM_KEYB_NOERROR"),temp_line.c_str(),dos.loaded_codepage);
+					WriteOut(MSG_Get("PROGRAM_KEYB_NOERROR"), layoutName.c_str(), dos.loaded_codepage);
 					break;
 				case KEYB_FILENOTFOUND:
-					WriteOut(MSG_Get("PROGRAM_KEYB_FILENOTFOUND"),temp_line.c_str());
+					WriteOut(MSG_Get("PROGRAM_KEYB_FILENOTFOUND"), layoutFile.c_str());
 					WriteOut(MSG_Get("PROGRAM_KEYB_SHOWHELP"));
 					break;
 				case KEYB_INVALIDFILE:
-					WriteOut(MSG_Get("PROGRAM_KEYB_INVALIDFILE"),temp_line.c_str());
+					WriteOut(MSG_Get("PROGRAM_KEYB_INVALIDFILE"), layoutFile.c_str());
 					break;
 				case KEYB_LAYOUTNOTFOUND:
-					WriteOut(MSG_Get("PROGRAM_KEYB_LAYOUTNOTFOUND"),temp_line.c_str(),tried_cp);
+					WriteOut(MSG_Get("PROGRAM_KEYB_LAYOUTNOTFOUND"), layoutName.c_str());
 					break;
 				case KEYB_INVALIDCPFILE:
-					WriteOut(MSG_Get("PROGRAM_KEYB_INVCPFILE"),temp_line.c_str());
-					WriteOut(MSG_Get("PROGRAM_KEYB_SHOWHELP"));
+					WriteOut(MSG_Get("PROGRAM_KEYB_INVCPFILE"), codepageFile.c_str());
 					break;
 				default:
 					LOG(LOG_DOSMISC,LOG_ERROR)("KEYB:Invalid returncode %x",keyb_error);
@@ -1527,16 +1538,16 @@
 	MSG_Add("PROGRAM_KEYB_INFO","Codepage %i has been loaded\n");
 	MSG_Add("PROGRAM_KEYB_INFO_LAYOUT","Codepage %i has been loaded for layout %s\n");
 	MSG_Add("PROGRAM_KEYB_SHOWHELP",
-		"\033[32;1mKEYB\033[0m [keyboard layout ID[ codepage number[ codepage file]]]\n\n"
+		"\033[32;1mKEYB\033[0m [layout file [keyboard layout ID[ codepage file[ codepage number]]]]\n\n"
 		"Some examples:\n"
 		"  \033[32;1mKEYB\033[0m: Display currently loaded codepage.\n"
-		"  \033[32;1mKEYB\033[0m sp: Load the spanish (SP) layout, use an appropriate codepage.\n"
-		"  \033[32;1mKEYB\033[0m sp 850: Load the spanish (SP) layout, use codepage 850.\n"
-		"  \033[32;1mKEYB\033[0m sp 850 mycp.cpi: Same as above, but use file mycp.cpi.\n");
+		"  \033[32;1mKEYB\033[0m auto sp: Load the spanish (SP) layout, use an appropriate codepage.\n"
+		"  \033[32;1mKEYB\033[0m auto sp auto 850: Load the spanish (SP) layout, use codepage 850.\n"
+		"  \033[32;1mKEYB\033[0m mykeybrd.sys sp mycp.cpi 850: Same as above, but use file mykeybrd.sys and mycp.cpi.\n");
 	MSG_Add("PROGRAM_KEYB_NOERROR","Keyboard layout %s loaded for codepage %i\n");
 	MSG_Add("PROGRAM_KEYB_FILENOTFOUND","Keyboard file %s not found\n\n");
 	MSG_Add("PROGRAM_KEYB_INVALIDFILE","Keyboard file %s invalid\n");
-	MSG_Add("PROGRAM_KEYB_LAYOUTNOTFOUND","No layout in %s for codepage %i\n");
+	MSG_Add("PROGRAM_KEYB_LAYOUTNOTFOUND","No layout %s\n");
 	MSG_Add("PROGRAM_KEYB_INVCPFILE","None or invalid codepage file for layout %s\n\n");
 
 	/*regular setup*/
Index: dosbox-0.74/src/dosbox.cpp
===================================================================
--- dosbox-0.74.orig/src/dosbox.cpp	2010-05-10 19:43:54.000000000 +0200
+++ dosbox-0.74/src/dosbox.cpp	2010-10-19 00:45:36.000000000 +0200
@@ -661,8 +661,17 @@
 	Pbool->Set_help("Enable UMB support.");
 
 	secprop->AddInitFunction(&DOS_KeyboardLayout_Init,true);
+	Pstring = secprop->Add_string("keyboardlayoutfile",Property::Changeable::WhenIdle, "auto");
+	Pstring->Set_help("Path to keyboard definition file (keyboard.sys or auto|none).");
+
 	Pstring = secprop->Add_string("keyboardlayout",Property::Changeable::WhenIdle, "auto");
-	Pstring->Set_help("Language code of the keyboard layout (or none).");
+	Pstring->Set_help("Code for the keyboard layout (or auto|none).");
+
+	Pstring = secprop->Add_string("codepagefile",Property::Changeable::WhenIdle, "auto");
+	Pstring->Set_help("Path to codepage definition file (ega.cpi or auto|none).");
+
+	Pstring = secprop->Add_string("codepage",Property::Changeable::WhenIdle, "auto");
+	Pstring->Set_help("Desired codepage.");
 
 	// Mscdex
 	secprop->AddInitFunction(&MSCDEX_Init);
