Task 340: .KLC File Format
Task 340: .KLC File Format
File Format Specifications for .KLC
The .KLC file format is a plain text format used by the Microsoft Keyboard Layout Creator (MSKLC) to define custom keyboard layouts for Windows. It is Unicode-encoded (typically UTF-8 or UTF-16) and consists of key-value pairs and tabular data sections describing the keyboard layout, locale, key mappings, and optional features like dead keys and ligatures. The format is not officially documented by Microsoft, but its structure can be inferred from examples and third-party parsers. Files start with a header section, followed by shift state definitions, key mappings, and key name lists. Optional sections include dead key definitions and ligature tables.
- List of all the properties of this file format intrinsic to its file system:
- KBD: The keyboard identifier code and description (e.g., KBD code "description").
- COPYRIGHT: The copyright notice.
- COMPANY: The company or creator name.
- LOCALENAME: The locale name (e.g., "bg-BG").
- LOCALEID: The locale ID (e.g., "00000402").
- VERSION: The version number (e.g., 1.0).
- SHIFTSTATE: Defines the shift states (columns for normal, shift, ctrl, etc.).
- LAYOUT: The main table of key mappings, including scancode (SC), virtual key (VK), caplock behavior (Cap), and characters for each shift state.
- KEYNAME: List of standard key codes and their names.
- KEYNAME_EXT: List of extended key codes and their names.
- KEYNAME_DEAD: List of dead key codes and their names (optional).
- DEADKEY: Definitions for dead keys, specifying the base key and combinations (optional).
- LIGATURE: Definitions for ligature characters (optional).
- Two direct download links for files of format .KLC:
- https://git.rozman.si/Simon/apple-keyboard-on-windows/src/branch/master/hr-apple.klc
- https://git.rozman.si/Simon/apple-keyboard-on-windows/src/branch/master/sl-apple.klc
- Ghost blog embedded html javascript that allows a user to drag n drop a file of format .KLC and it will dump to screen all these properties.
- Python class that can open any file of format .KLC and decode read and write and print to console all the properties from the above list.
import json
class KLCParser:
def __init__(self):
self.properties = {}
def read(self, filepath):
with open(filepath, 'r', encoding='utf-16' if filepath.endswith('.klc') else 'utf-8') as f:
content = f.read()
lines = content.split('\n')
current_section = None
for line in lines:
line = line.strip()
if not line:
continue
if line.isupper():
current_section = line
self.properties[current_section] = []
elif current_section:
self.properties[current_section].append(line)
else:
parts = line.split(maxsplit=1)
if len(parts) == 2:
key, value = parts
self.properties[key] = value
# Post-processing for specific sections
if 'SHIFTSTATE' in self.properties:
self.properties['SHIFTSTATE'] = [l.split('//')[0].strip() for l in self.properties['SHIFTSTATE'] if l[0].isdigit()]
if 'LAYOUT' in self.properties:
self.properties['LAYOUT'] = [l.split('//')[0].strip() for l in self.properties['LAYOUT'] if l[0].isalnum()]
# Handle DEADKEY, LIGATURE if present
def print_properties(self):
print(json.dumps(self.properties, indent=4))
def write(self, filepath):
with open(filepath, 'w', encoding='utf-16') as f:
for key, value in self.properties.items():
if isinstance(value, str):
f.write(f"{key} {value}\n")
elif isinstance(value, list):
f.write(f"{key}\n")
for item in value:
f.write(f"{item}\n")
f.write("ENDKBD\n") # Optional end marker if needed
# Example usage
if __name__ == "__main__":
parser = KLCParser()
parser.read('example.klc')
parser.print_properties()
parser.write('output.klc')
- Java class that can open any file of format .KLC and decode read and write and print to console all the properties from the above list.
import java.io.*;
import java.util.*;
public class KLCParser {
private Map<String, Object> properties = new HashMap<>();
public void read(String filepath) throws IOException {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(filepath), "UTF-16"))) {
String line;
String currentSection = null;
while (line = reader.readLine() != null) {
line = line.trim();
if (line.isEmpty()) continue;
if (line.toUpperCase().equals(line)) {
currentSection = line;
properties.put(currentSection, new ArrayList<String>());
} else if (currentSection != null) {
((List<String>) properties.get(currentSection)).add(line);
} else {
String[] parts = line.split("\\s+", 2);
if (parts.length == 2) properties.put(parts[0], parts[1]);
}
}
}
// Post-processing
if (properties.containsKey("SHIFTSTATE")) {
List<String> shift = (List<String>) properties.get("SHIFTSTATE");
shift.removeIf(l -> !Character.isDigit(l.charAt(0)));
shift.replaceAll(l -> l.split("//")[0].trim());
}
if (properties.containsKey("LAYOUT")) {
List<String> layout = (List<String>) properties.get("LAYOUT");
layout.removeIf(l -> !Character.isAlphabetic(l.charAt(0)) && !Character.isDigit(l.charAt(0)));
layout.replaceAll(l -> l.split("//")[0].trim());
}
}
public void printProperties() {
System.out.println(properties);
}
public void write(String filepath) throws IOException {
try (PrintWriter writer = new PrintWriter(new FileWriter(filepath, java.nio.charset.StandardCharsets.UTF_16))) {
for (Map.Entry<String, Object> entry : properties.entrySet()) {
if (entry.getValue() instanceof String) {
writer.println(entry.getKey() + " " + entry.getValue());
} else if (entry.getValue() instanceof List) {
writer.println(entry.getKey());
for (String item : (List<String>) entry.getValue()) {
writer.println(item);
}
}
}
writer.println("ENDKBD");
}
}
public static void main(String[] args) throws IOException {
KLCParser parser = new KLCParser();
parser.read("example.klc");
parser.printProperties();
parser.write("output.klc");
}
}
- Javascript class that can open any file of format .KLC and decode read and write and print to console all the properties from the above list.
class KLCParser {
constructor() {
this.properties = {};
}
async read(filepath) {
// For browser, use Fetch or FileReader; here assuming Node.js for console
const fs = require('fs');
const content = fs.readFileSync(filepath, 'utf16le');
const lines = content.split('\n').map(line => line.trim());
let currentSection = null;
lines.forEach(line => {
if (!line) return;
if (line.toUpperCase() === line) {
currentSection = line;
this.properties[currentSection] = [];
} else if (currentSection) {
this.properties[currentSection].push(line);
} else {
const [key, ...value] = line.split(/\s+/);
this.properties[key] = value.join(' ');
}
});
// Post-processing
if (this.properties.SHIFTSTATE) {
this.properties.SHIFTSTATE = this.properties.SHIFTSTATE.filter(l => /\d/.test(l[0])).map(l => l.split('//')[0].trim());
}
if (this.properties.LAYOUT) {
this.properties.LAYOUT = this.properties.LAYOUT.filter(l => /[0-9a-fA-F]/.test(l[0])).map(l => l.split('//')[0].trim());
}
}
printProperties() {
console.log(JSON.stringify(this.properties, null, 2));
}
write(filepath) {
const fs = require('fs');
let output = '';
for (const [key, value] of Object.entries(this.properties)) {
if (typeof value === 'string') {
output += `${key} ${value}\n`;
} else if (Array.isArray(value)) {
output += `${key}\n`;
value.forEach(item => output += `${item}\n`);
}
}
output += 'ENDKBD\n';
fs.writeFileSync(filepath, output, 'utf16le');
}
}
// Example usage (Node.js)
(async () => {
const parser = new KLCParser();
await parser.read('example.klc');
parser.printProperties();
parser.write('output.klc');
})();
- C class that can open any file of format .KLC and decode read and write and print to console all the properties from the above list.
Since C does not have built-in classes, I'll use C++ for this implementation.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <sstream>
class KLCParser {
private:
std::map<std::string, std::vector<std::string>> properties;
public:
void read(const std::string& filepath) {
std::ifstream file(filepath, std::ios::binary);
if (!file) {
std::cerr << "Cannot open file" << std::endl;
return;
}
std::string line;
std::string currentSection;
while (std::getline(file, line)) {
// Trim line (simple trim)
line.erase(0, line.find_first_not_of(" \t"));
line.erase(line.find_last_not_of(" \t") + 1);
if (line.empty()) continue;
bool isSection = true;
for (char c : line) {
if (!isupper(c)) {
isSection = false;
break;
}
}
if (isSection) {
currentSection = line;
properties[currentSection] = {};
} else if (!currentSection.empty()) {
properties[currentSection].push_back(line);
} else {
std::istringstream iss(line);
std::string key, value;
iss >> key;
std::getline(iss, value);
properties[key] = {value};
}
}
// Post-processing
if (properties.count("SHIFTSTATE")) {
auto& shift = properties["SHIFTSTATE"];
std::vector<std::string> newShift;
for (auto& l : shift) {
if (isdigit(l[0])) {
size_t pos = l.find("//");
if (pos != std::string::npos) l = l.substr(0, pos);
l.erase(l.find_last_not_of(" \t") + 1);
newShift.push_back(l);
}
}
shift = newShift;
}
if (properties.count("LAYOUT")) {
auto& layout = properties["LAYOUT"];
std::vector<std::string> newLayout;
for (auto& l : layout) {
if (isalnum(l[0])) {
size_t pos = l.find("//");
if (pos != std::string::npos) l = l.substr(0, pos);
l.erase(l.find_last_not_of(" \t") + 1);
newLayout.push_back(l);
}
}
layout = newLayout;
}
}
void printProperties() {
for (const auto& pair : properties) {
std::cout << pair.first << ":" << std::endl;
for (const auto& val : pair.second) {
std::cout << " " << val << std::endl;
}
}
}
void write(const std::string& filepath) {
std::ofstream file(filepath, std::ios::binary);
if (!file) {
std::cerr << "Cannot open file for writing" << std::endl;
return;
}
for (const auto& pair : properties) {
if (pair.second.size() == 1 && !pair.second[0].empty()) {
file << pair.first << " " << pair.second[0] << "\n";
} else {
file << pair.first << "\n";
for (const auto& val : pair.second) {
file << val << "\n";
}
}
}
file << "ENDKBD\n";
}
};
int main() {
KLCParser parser;
parser.read("example.klc");
parser.printProperties();
parser.write("output.klc");
return 0;
}