Merge pull request #1696 from keymanapp/developer-kmanalyze-repick

[Developer] Add kmanalyze tool (rebased and repicked)
This commit is contained in:
Marc Durdin 2019-03-29 08:53:58 +11:00 committed by GitHub
commit fa7d893ab3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 1038 additions and 2 deletions

View file

@ -3,7 +3,7 @@
#
TARGETS=kmcmpdll kmcomp kmconvert tike samples setup inst
TARGETS=kmcmpdll kmcomp kmanalyze kmconvert tike samples setup inst
MANIFESTS=kmcomp tike setup
!include ..\Header.mak
@ -22,6 +22,10 @@ kmcomp: kmcmpdll
cd $(ROOT)\src\developer\kmcomp
$(MAKE) $(TARGET)
kmanalyze:
cd $(ROOT)\src\developer\kmanalyze
$(MAKE) $(TARGET)
kmconvert:
cd $(ROOT)\src\developer\kmconvert
$(MAKE) $(TARGET)

View file

@ -0,0 +1,2 @@
Debug/
.vs/

View file

@ -0,0 +1,26 @@
#
# KMAnalyze Makefile
#
!include ..\..\Defines.mak
build: version.res dirs
$(MSBUILD) kmanalyze.sln $(MSBUILD_BUILD)
$(COPY) $(TARGET_PATH)\kmanalyze.exe $(PROGRAM)\developer
clean: def-clean
$(MSBUILD) kmanalyze.sln $(MSBUILD_CLEAN)
signcode:
$(SIGNCODE) /d "Keyman Developer Keyboard Analyzer" $(PROGRAM)\developer\kmanalyze.exe
backup:
$(WZZIP) $(BUILD)\developer\kmanalyze.exe $(BACKUPDEFAULTS) kmanalyze.exe
test-manifest:
@rem
#install:
# $(COPY) $(PROGRAM)\developer\kmanalyze.exe "$(INSTALLPATH_KEYMANDEVELOPER)\kmanalyze.exe"
!include ..\..\Target.mak

View file

@ -0,0 +1,692 @@
// kmanalyze.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include "pch.h"
#include "crc32.h"
#include <iostream>
#include <vector>
#include <codecvt>
#include <locale>
BOOL LoadKeyboard(LPSTR fileName, LPKEYBOARD *lpKeyboard);
BOOL VerifyChecksum(LPBYTE buf, LPDWORD CheckSum, DWORD sz);
void Err(const char *p);
int DoKeyboardAnalysis(LPKEYBOARD kbd, char *keyboardID, char *keyboardJSFilename, char *outputfilename);
void MapVirtualKeys(void);
struct TEST {
UINT key;
UINT shift;
std::wstring context;
};
typedef std::vector<TEST> TESTS;
enum GROUPREFTYPE { rule, match, nomatch };
struct GROUPTREE;
struct GROUPREF {
GROUPREFTYPE type;
GROUPTREE* target;
LPKEY rule;
};
struct GROUPTREE {
LPGROUP group;
std::vector<GROUPREF> refs;
};
TESTS *DoGroupAnalysis(LPKEYBOARD kbd, LPGROUP gp, std::vector<LPGROUP> & tree, TEST *test);
wchar_t VKToChar[256][2];
UINT CharToVK[256];
UINT CharToShift[256];
int main(int argc, char *argv[])
{
LPKEYBOARD kbd;
char buf[_MAX_PATH], drive[_MAX_DRIVE], dir[_MAX_DIR], filename[_MAX_FNAME], ext[_MAX_EXT], jsfilename[_MAX_PATH];
if (argc < 2 || !strcmp(argv[1], "--help"))
{
puts("KMANALYZE: Extract rules from a Keyman Desktop .kmx keyboard to use for building automated tests");
puts("(C) SIL International");
puts("Usage: KMANALYZE <filename> [outputfilename]\n");
puts("Will create a keyboard.tests from keyboard.js; if outputfilename is not specified,");
puts("then will put the output file in the same folder as filename.");
return 1;
}
MapVirtualKeys();
if (!LoadKeyboard(argv[1], &kbd)) return 2;
_splitpath_s(argv[1], drive, dir, filename, ext);
if (argc >= 3) {
strcpy_s(buf, argv[2]);
}
else {
_makepath_s(buf, drive, dir, filename, ".tests");
}
_makepath_s(jsfilename, drive, dir, filename, ".js");
int n = DoKeyboardAnalysis(kbd, filename, jsfilename, buf);
delete kbd;
return n;
}
void Err(const char *p)
{
printf("Fatal Error: %s\n", p);
}
BOOL LoadKeyboard(LPSTR fileName, LPKEYBOARD *lpKeyboard)
{
DWORD sz, i, j;
LPBYTE buf;
HANDLE hFile;
PCOMP_KEYBOARD ckbp;
PCOMP_GROUP cgp;
PCOMP_STORE csp;
PCOMP_KEY ckp;
LPKEYBOARD kbp;
LPGROUP gp;
LPSTORE sp;
LPKEY kp;
if (!lpKeyboard)
{
Err("Internal error 001");
return FALSE;
}
if (!fileName)
{
Err("Bad Filename");
return FALSE;
}
hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
Err("Could not open file");
return FALSE;
}
sz = GetFileSize(hFile, NULL);
buf = new BYTE[sz];
if (!buf)
{
CloseHandle(hFile);
Err("Could not allocate memory");
return FALSE;
}
ReadFile(hFile, buf, sz, &sz, NULL);
CloseHandle(hFile);
kbp = (LPKEYBOARD)buf;
ckbp = (PCOMP_KEYBOARD)buf;
if (kbp->dwIdentifier != FILEID_COMPILED) { delete buf; Err("errNotFileID"); return FALSE; }
/* Check file version */
if (ckbp->dwFileVersion < VERSION_MIN ||
ckbp->dwFileVersion > VERSION_MAX)
{
/* Old or new version -- identify the desired program version */
if (VerifyChecksum(buf, &kbp->dwCheckSum, sz))
{
kbp->dpStoreArray = (LPSTORE)(buf + ckbp->dpStoreArray);
for (sp = kbp->dpStoreArray, i = 0; i < kbp->cxStoreArray; i++, sp++)
if (sp->dwSystemID == TSS_COMPILEDVERSION)
{
char buf2[256];
wsprintf(buf2, "Wrong File Version: file version is %ls", ((PBYTE)kbp) + (DWORD)sp->dpString);
delete buf;
Err(buf2);
return FALSE;
}
}
delete buf; Err("Unknown File Version: try using the latest version of KMDECOMP");
return FALSE;
}
if (!VerifyChecksum(buf, &kbp->dwCheckSum, sz)) { delete buf; Err("Bad Checksum in file"); return FALSE; }
kbp->dpStoreArray = (LPSTORE)(buf + ckbp->dpStoreArray);
kbp->dpGroupArray = (LPGROUP)(buf + ckbp->dpGroupArray);
//kbp->dpName = (PWSTR) (buf + ckbp->dpName);
//kbp->dpCopyright = (PWSTR) (buf + ckbp->dpCopyright);
//kbp->dpMessage = (PWSTR) (buf + ckbp->dpMessage);
//kbp->dpLanguageName = (PWSTR) (buf + ckbp->dpLanguageName);
for (sp = kbp->dpStoreArray, csp = (PCOMP_STORE)sp, i = 0; i < kbp->cxStoreArray; i++, sp++, csp++)
{
if (csp->dpName > 0) sp->dpName = (PWSTR)(buf + csp->dpName); else sp->dpName = NULL;
sp->dpString = (PWSTR)(buf + csp->dpString);
}
for (gp = kbp->dpGroupArray, cgp = (PCOMP_GROUP)gp, i = 0; i < kbp->cxGroupArray; i++, gp++, cgp++)
{
if (cgp->dpName > 0) gp->dpName = (PWSTR)(buf + cgp->dpName); else gp->dpName = NULL;
gp->dpKeyArray = (LPKEY)(buf + cgp->dpKeyArray);
if (cgp->dpMatch != NULL) gp->dpMatch = (PWSTR)(buf + cgp->dpMatch);
if (cgp->dpNoMatch != NULL) gp->dpNoMatch = (PWSTR)(buf + cgp->dpNoMatch);
for (kp = gp->dpKeyArray, ckp = (PCOMP_KEY)kp, j = 0; j < gp->cxKeyArray; j++, kp++, ckp++)
{
kp->dpOutput = (PWSTR)(buf + ckp->dpOutput);
kp->dpContext = (PWSTR)(buf + ckp->dpContext);
}
}
*lpKeyboard = kbp;
return TRUE;
}
BOOL VerifyChecksum(LPBYTE buf, LPDWORD CheckSum, DWORD sz)
{
DWORD tempcs;
tempcs = *CheckSum;
*CheckSum = 0;
BuildCRCTable();
return tempcs == CalculateBufferCRC(sz, buf);
}
//void DoContextAnalysis(LPKEYBOARD kbd, GROUPTREE *gpref, std::vector<LPGROUP> tree, LPKEY kp, PWCHAR pc, GROUPREFTYPE type) {
//}
TESTS *DoGroupAnalysis(LPKEYBOARD kbd, LPGROUP gp, std::vector<LPGROUP> & tree, TEST *base_test) {
//
// Prevent recursion
//
std::vector<LPGROUP>::iterator it = std::find(tree.begin(), tree.end(), gp);
if(it != tree.end()) {
puts("Recursive groups are not yet supported");
return NULL;
}
tree.push_back(gp);
//printf("%*.*sEntering group %d [depth %d]\n", tree.size(), tree.size(), " ", gpref->group - kbd->dpGroupArray + 1, tree.size());
LPKEY kp = gp->dpKeyArray;
// Set of tests for this group.
TESTS *tests = new TESTS;
for (DWORD i = 0; i < gp->cxKeyArray; i++, kp++) {
// Set of expanding tests for this rule
TESTS *new_tests = NULL;
TEST t0;
if (gp->fUsingKeys && base_test && base_test->key != 0 && (base_test->key != kp->Key || base_test->shift != kp->ShiftFlags)) {
// We are in a subgroup that uses keys, and this rule's key doesn't match the parent group's test's key.
continue;
}
std::wstring context;
for (PWCHAR pc = kp->dpContext; pc && *pc; pc = incxstr(pc)) {
if (*pc == UC_SENTINEL) {
switch (*(pc + 1)) {
case CODE_ANY:
{
PWCHAR ps = kbd->dpStoreArray[*(pc + 2) - 1].dpString;
// TODO: Support any character in store
if (ps[0] == UC_SENTINEL) break;
context += ps[0];
if (Uni_IsSurrogate1(ps[0])) context += ps[1];
}
break;
case CODE_NUL:
// TODO: SUpport nul at start of context
break;
case CODE_DEADKEY:
// TODO: Support deadkeys for testing
break;
case CODE_CONTEXTEX:
break;
case CODE_NOTANY:
break;
}
}
else {
context += *pc;
if (Uni_IsSurrogate1(*pc)) context += *(pc + 1);
}
}
if (base_test) {
std::wstring c1 = context.length() > base_test->context.length() ? context : base_test->context;
//std::wstring c2 = context.length() > base_test->context.length() ? base_test->context : context;
//if(c1.compare(c1.length() - c2.length(), c2.length(), c2) != 0) {
// TODO: We are not comparing like with like ... so continue; but this naive test does not take into account
// stores, which we need to, in order to make this work well.
//}
// TODO: For now we are assuming that the contexts are congruent, so we take the longer one for our test.
// TODO: In reality, we could probably do both the longer and shorter one as separate tests...
//TODO: merge_context(context);
context = c1;
}
t0.context = context;
if (!gp->fUsingKeys) {
if (base_test) {
t0.key = base_test->key;
t0.shift = base_test->shift;
}
}
else {
t0.key = kp->Key;
t0.shift = kp->ShiftFlags;
}
BOOL found = FALSE;
for (PWCHAR pc = kp->dpOutput; pc && *pc; pc = incxstr(pc)) {
if (*pc == UC_SENTINEL && *(pc + 1) == CODE_USE) {
//printf("%*.*s: Matched rule %d of group %d\n", tree.size(), tree.size(), " ", kp ? kp - gpref->group->dpKeyArray + 1 : -1, gpref->group - kbd->dpGroupArray + 1);
LPGROUP gpTarget = &kbd->dpGroupArray[*(pc + 2) - 1];
new_tests = DoGroupAnalysis(kbd, gpTarget, tree, &t0);
if (!new_tests) return NULL;
// TODO: Then multiplying new_tests for each subsequent group I guess!?
found = TRUE;
break;
}
}
if (!found) {
// TODO: Support use() in rule and use() in match
// check for use in match or extra
PWCHAR pc = gp->dpMatch;
if (pc && *pc == UC_SENTINEL && *(pc + 1) == CODE_USE) {
new_tests = DoGroupAnalysis(kbd, &kbd->dpGroupArray[*(pc + 2) - 1], tree, &t0);
if (!new_tests) return NULL;
// TODO: JUST A SINGLE GROUP FOR NOW
}
}
tests->push_back(t0);
if (new_tests) {
for (size_t i = 0; i < new_tests->size(); i++)
tests->push_back(new_tests->at(i));
delete new_tests;
}
//t0.context = "";
//PWC
//t0.context = kp->dpContext;
//for (int i = 0; i < new_tests.size(); i++) {
//DoContextAnalysis(kbd, gp, tree, kp, kp->dpOutput, rule, gpref->group->dpMatch);
}
PWCHAR pc = gp->dpNoMatch;
if (pc && *pc == UC_SENTINEL && *(pc + 1) == CODE_USE) {
TESTS *new_tests = DoGroupAnalysis(kbd, &kbd->dpGroupArray[*(pc+2)-1], tree, base_test);
if (!new_tests) return NULL;
for (size_t i = 0; i < new_tests->size(); i++)
tests->push_back(new_tests->at(i));
delete new_tests;
// TODO: JUST A SINGLE GROUP FOR NOW
}
//printf("%*.*sExiting group %d [depth %d]\n", tree.size(), tree.size(), " ", gpref->group - kbd->dpGroupArray + 1, tree.size());
tree.pop_back();
return tests;
}
void PrintTree(LPKEYBOARD kbd, GROUPTREE *t, int depth);
std::string unicode_escape(std::wstring s) {
std::string r = "\"";
for (auto ch = s.begin(); ch != s.end(); ch++) {
char buf[16];
sprintf_s(buf, "\\u%04.4x", *ch);
r += buf;
}
r += "\"";
return r;
}
#include <vkeys.h>
std::wstring GetKeyName(UINT key) {
WCHAR buf[64];
// if (key < 256) {
// wsprintfW(buf, L"keyCodes.%s /* 0x%x */", VKeyNames[key], key);
// }
// else {
wsprintfW(buf, L"0x%x", key);
// }
std::wstring str(buf);
return str;
}
struct ModifierNames {
PCWSTR name;
UINT modifier;
};
// see kmwosk.ts
ModifierNames modifierNames[] = {
{ L"LCTRL", 0x0001 },
{ L"RCTRL", 0x0002 },
{ L"LALT", 0x0004 },
{ L"RALT", 0x0008 },
{ L"SHIFT", 0x0010 },
{ L"CTRL", 0x0020 },
{ L"ALT", 0x0040 },
{ L"CAPS", 0x0100 },
{ L"NO_CAPS", 0x0200 },
{ L"NUM_LOCK", 0x0400 },
{ L"NO_NUM_LOCK", 0x0800 },
{ L"SCROLL_LOCK", 0x1000 },
{ L"NO_SCROLL_LOCK", 0x2000 },
{ L"VIRTUAL_KEY", 0x4000 },
{ NULL, 0 }
};
std::wstring GetModifierName(UINT modifier) {
modifier &= ~ISVIRTUALKEY; // we don't need to dump this in every rule
std::wstring str;
WCHAR buf[64];
UINT originalModifier = modifier;
for (int i = 0; modifierNames[i].name; i++) {
if (modifier & modifierNames[i].modifier) {
wsprintfW(buf, L"modCodes.%s", modifierNames[i].name);
if (str.size()) str += L" | ";
str += buf;
modifier &= ~modifierNames[i].modifier;
}
}
if (modifier) {
wsprintfW(buf, L"%x", modifier);
if (str.size()) str += L" | ";
str += buf;
}
wsprintfW(buf, L" /* 0x%x */", originalModifier);
if (!str.size()) str += L"0";
str += buf;
return str;
}
void PrintTests(TESTS *tests, char *keyboardID, wchar_t *keyboardName, char *keyboardJSFilename, char *outputfilename) {
FILE *fp;
fopen_s(&fp, outputfilename, "wt");
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
//std::wstring keyboardName_t = keyboardName;
fprintf(fp,
/*"(function(){\n"
"\"use strict\";\n"
"var modCodes = testRunner.modCodes;\n"
"var keyCodes = testRunner.keyCodes;\n"
"testRunner.register(*/"{\n"
" \"keyboard\": {\n"
" \"id\": \"%s\",\n"
//" \"name\" : \"%s\",\n" //TODO: utf8
//" \"filename\" : \"%s\",\n"
" \"languages\" : [{\n"
" \"id\": \"en\",\n"
" \"name\" : \"English\",\n"
" \"region\" : \"Europe\"\n"
" }]\n"
" },\n"
" \"inputTests\": {\n",
keyboardID
//utf8_conv.to_bytes(keyboardName).c_str(),
//keyboardJSFilename
);
for (size_t i = 0; i < tests->size(); i++) {
UINT key = tests->at(i).key, shift = tests->at(i).shift;
if (!(shift & ISVIRTUALKEY)) {
shift = CharToShift[key] | ISVIRTUALKEY;
key = CharToVK[key];
}
std::wstring keyName = GetKeyName(key);
std::wstring modifierName = GetModifierName(shift);
fprintf(fp, " \"%d\": {\"key\": %d", i, key); // keyName.c_str());
if (shift != ISVIRTUALKEY && shift != 0)
fprintf(fp, ", \"modifier\": %d", shift & ~ISVIRTUALKEY); // %ws", modifierName.c_str());
if (tests->at(i).context.size())
fprintf(fp, ", \"context\": %s", unicode_escape(tests->at(i).context).c_str());
fprintf(fp, "}%s\n", (i == tests->size() - 1 ? "" : ","));
}
fprintf(fp,
" }\n"
"}"/*);\n"
"})();\n"*/
);
fclose(fp);
}
void RemoveDuplicateTests(TESTS *tests) {
// TODO: sort tests by key, modifier, context
// Then remove duplicates
}
int DoKeyboardAnalysis(LPKEYBOARD kbd, char *keyboardID, char *keyboardJSFilename, char *outputfilename) {
std::vector<std::vector<GROUPREF>> groupPaths;
//GROUPTREE tree;
LPGROUP gp = &kbd->dpGroupArray[kbd->StartGroup[1]]; // Unicode start group
std::vector<LPGROUP> tt;
// each rule in the base group is a base rule. Then, each
// rule can be expanded out by the use() tree from the rule.
TESTS *tests = DoGroupAnalysis(kbd, gp, tt, NULL);
if (!tests) {
// Error exit
return 3;
}
wchar_t *keyboardName = NULL, keyboardNameBuf[32];
for (UINT i = 0; i < kbd->cxStoreArray; i++) {
if (kbd->dpStoreArray[i].dwSystemID == TSS_NAME) {
keyboardName = kbd->dpStoreArray[i].dpString;
}
}
if (!keyboardName) {
wsprintfW(keyboardNameBuf, L"%s", keyboardID);
keyboardName = keyboardNameBuf;
}
RemoveDuplicateTests(tests);
PrintTests(tests, keyboardID, keyboardName, keyboardJSFilename, outputfilename);
delete tests;
// print the tree
//PrintTree(kbd, &tree, 0);
return 0;
}
DWORD NextUTF32(PWCHAR pc) {
if (Uni_IsSurrogate1(*pc)) {
return Uni_SurrogateToUTF32(*pc, *(pc + 1));
}
return *pc;
}
/*
void PrintRule(LPKEYBOARD kbd, LPKEY kp) {
LPSTORE sp;
std::vector<DWORD> context, output;
// Always take first char in each referenced store
for (PWCHAR pc = kp->dpContext; pc && *pc; pc = incxstr(pc)) {
if (*pc == UC_SENTINEL) {
switch (*(pc + 1)) {
case CODE_ANY:
sp = &kbd->dpStoreArray[*(pc + 2) - 1];
context.push_back(NextUTF32(sp->dpString));
break;
case CODE_NOTANY:
assert(FALSE); //TODO
case CODE_INDEX:
assert(FALSE); //TODO
case CODE_DEADKEY:
case CODE_EXTENDED: p += 2; while (*p != UC_SENTINEL_EXTENDEDEND) p++; return p + 1;
case CODE_CLEARCONTEXT: return p + 1;
case CODE_CALL: return p + 1;
case CODE_CONTEXTEX: return p + 1;
case CODE_IFOPT: return p + 3;
case CODE_IFSYSTEMSTORE: return p + 3;
case CODE_SETOPT: return p + 2;
case CODE_SETSYSTEMSTORE: return p + 2;
case CODE_RESETOPT: return p + 1;
case CODE_SAVEOPT: return p + 1;
}
}
else {
// Character (either 1 or 2 word)
}
}
}
*/
int groupindex(LPKEYBOARD kbd, LPGROUP gp) {
return gp - kbd->dpGroupArray;
}
void PrintTree(LPKEYBOARD kbd, GROUPTREE *t, int depth) {
if (t->group->dpName)
printf("%*.*s%ws\n", depth * 2, depth * 2, " ", t->group->dpName);
else
printf("%*.*sgroup%d\n", depth * 2, depth * 2, " ", groupindex(kbd, t->group));
// LPKEY kp = t->group->dpKeyArray;
// for (DWORD i = 0; i < t->group->cxKeyArray; i++) {
// AnalyzeRule(kp);
// }
for (size_t i = 0; i < t->refs.size(); i++) {
PrintTree(kbd, t->refs[i].target, (depth + 1));
}
}
// MapVirtualKeys is copied from syskbd.cpp (keyman32)
#define VK_COLON 0xBA
#define VK_EQUAL 0xBB
#define VK_COMMA 0xBC
#define VK_HYPHEN 0xBD
#define VK_PERIOD 0xBE
#define VK_SLASH 0xBF
#define VK_ACCENT 0xC0
#define VK_LBRKT 0xDB
#define VK_BKSLASH 0xDC
#define VK_RBRKT 0xDD
#define VK_QUOTE 0xDE
#define VK_xDF 0xDF
WCHAR MapVirtualKeys(WORD keyCode, UINT shiftFlags)
{
char shiftedDigit[] = ")!@#$%^&*(";
int n, Shift;
if (shiftFlags & (LCTRLFLAG | RCTRLFLAG | LALTFLAG | RALTFLAG)) return 0;
if (keyCode >= '0' && keyCode <= '9')
{
n = keyCode - '0';
return ((shiftFlags & K_SHIFTFLAG) ? shiftedDigit[n] : keyCode);
}
if (keyCode >= 'A' && keyCode <= 'Z')
{
Shift = (shiftFlags & K_SHIFTFLAG);
if (shiftFlags & (CAPITALFLAG)) Shift = !Shift;
return (Shift ? keyCode : keyCode + 32);
}
if (keyCode >= VK_NUMPAD0 && keyCode <= VK_NUMPAD9)
{
if (!(shiftFlags & NUMLOCKFLAG)) return 0;
return keyCode - (VK_NUMPAD0 - '0');
}
Shift = (shiftFlags & K_SHIFTFLAG);
switch (keyCode)
{
case VK_ACCENT:
return Shift ? '~' : '`';
case VK_HYPHEN:
return Shift ? '_' : '-';
case VK_EQUAL:
return Shift ? '+' : '=';
case VK_BKSLASH:
case 0xE2: // I5332
return Shift ? '|' : 92;
case VK_LBRKT:
return Shift ? '{' : '[';
case VK_RBRKT:
return Shift ? '}' : ']';
case VK_COLON:
return Shift ? ':' : ';';
case VK_QUOTE:
return Shift ? '"' : 39;
case VK_COMMA:
return Shift ? '<' : ',';
case VK_PERIOD:
return Shift ? '>' : '.';
case VK_SLASH:
return Shift ? '?' : '/';
case VK_SPACE:
return ' ';
}
return 0;
//keyCode;
}
void MapVirtualKeys(void) {
for (int i = 0; i < 256; i++) {
VKToChar[i][0] = MapVirtualKeys(i, 0);
VKToChar[i][1] = MapVirtualKeys(i, K_SHIFTFLAG);
CharToVK[VKToChar[i][0]] = i;
CharToVK[VKToChar[i][1]] = i;
CharToShift[VKToChar[i][0]] = 0;
CharToShift[VKToChar[i][1]] = K_SHIFTFLAG;
}
}

View file

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28307.136
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmanalyze", "kmanalyze.vcxproj", "{4EAE6DFD-E18A-493D-A00F-4846A2593F56}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4EAE6DFD-E18A-493D-A00F-4846A2593F56}.Debug|Win32.ActiveCfg = Debug|Win32
{4EAE6DFD-E18A-493D-A00F-4846A2593F56}.Debug|Win32.Build.0 = Debug|Win32
{4EAE6DFD-E18A-493D-A00F-4846A2593F56}.Debug|x64.ActiveCfg = Debug|x64
{4EAE6DFD-E18A-493D-A00F-4846A2593F56}.Debug|x64.Build.0 = Debug|x64
{4EAE6DFD-E18A-493D-A00F-4846A2593F56}.Release|Win32.ActiveCfg = Release|Win32
{4EAE6DFD-E18A-493D-A00F-4846A2593F56}.Release|Win32.Build.0 = Release|Win32
{4EAE6DFD-E18A-493D-A00F-4846A2593F56}.Release|x64.ActiveCfg = Release|x64
{4EAE6DFD-E18A-493D-A00F-4846A2593F56}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CFA573C6-8EED-4118-A86B-05964F4D34D5}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,182 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{4EAE6DFD-E18A-493D-A00F-4846A2593F56}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>kmanalyze</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\windows\src\global\inc</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\windows\src\global\inc</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\windows\src\global\inc</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);$(KEYMAN_ROOT)\windows\src\global\inc</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\..\..\Projects\keyman\open\windows\src\global\inc\keyman64.h" />
<ClInclude Include="pch.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\global\vc\crc32.cpp" />
<ClCompile Include="..\..\global\vc\xstring.cpp" />
<ClCompile Include="kmanalyze.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="version.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\Projects\keyman\open\windows\src\global\inc\keyman64.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="kmanalyze.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\global\vc\xstring.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\global\vc\crc32.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="version.rc">
<Filter>Source Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,5 @@
// pch.cpp: source file corresponding to pre-compiled header; necessary for compilation to succeed
#include "pch.h"
// In general, ignore this file, but keep it around if you are using pre-compiled headers.

View file

@ -0,0 +1,20 @@
// Tips for Getting Started:
// 1. Use the Solution Explorer window to add/manage files
// 2. Use the Team Explorer window to connect to source control
// 3. Use the Output window to see build output and other messages
// 4. Use the Error List window to view errors
// 5. Go to Project > Add New Item to create new code files, or Project > Add Existing Item to add existing code files to the project
// 6. In the future, to open this project again, go to File > Open > Project and select the .sln file
#ifndef PCH_H
#define PCH_H
// TODO: add headers that you want to pre-compile here
#define STRICT
#define _KEYMAN64_LIGHT
#include <windows.h>
#include <string.h>
#include <stdio.h>
#include "keyman64.h"
#endif //PCH_H

View file

@ -0,0 +1,30 @@
1 VERSIONINFO
FILEVERSION 11,0,1500,0
PRODUCTVERSION 11,0,1500,0
FILEFLAGSMASK 0x3fL
FILEFLAGS 0x0L
FILEOS 0x4L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "0C0904E4"
BEGIN
VALUE "CompanyName", "SIL International\0"
VALUE "FileDescription", "Keyman Developer Keyboard Analyzer\0"
VALUE "FileVersion", "11.0.1500.0\0"
VALUE "InternalName", "KMANALYZE\0"
VALUE "LegalCopyright", "© SIL International\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "KMANALYZE.EXE\0"
VALUE "ProductName", "Keyman Developer\0"
VALUE "ProductVersion", "11.0.1500.0\0"
VALUE "Comments", "\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0xc09, 1252
END
END

View file

@ -1,5 +1,5 @@
static PWSTR VKeyNames[256] = {
static PCWSTR VKeyNames[256] = {
// Key Codes
L"K_?00", // &H0
L"K_LBUTTON", // &H1