mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 08:25:32 +00:00
- split keyboard loading into loading KMX file into blob and then loading the keyboard processor from the blob. - deprecate `km_core_keyboard_load` - move file access next to deprecated method. This is now the only place that loads a file in Core; unit tests have some more places that load files. - introduce GTest and add unit tests for loading from blob Part-of: #11293
315 lines
8.6 KiB
C++
315 lines
8.6 KiB
C++
/*
|
|
Copyright: Copyright (C) 2003-2018 SIL International.
|
|
Authors: mcdurdin
|
|
*/
|
|
#include "kmx_processevent.h"
|
|
#include <assert.h>
|
|
#include "kmx_file_validator.hpp"
|
|
|
|
using namespace km::core;
|
|
using namespace kmx;
|
|
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
#include <share.h>
|
|
#endif
|
|
|
|
KMX_BOOL KMX_ProcessEvent::Load(PKMX_BYTE buf, size_t sz) {
|
|
if(!LoadKeyboardFromBlob(buf, sz, &m_keyboard.Keyboard))
|
|
return FALSE; // I5136
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
const int km::core::kmx::CODE__SIZE[] = {
|
|
-1, // undefined 0x00
|
|
1, // CODE_ANY 0x01
|
|
2, // CODE_INDEX 0x02
|
|
0, // CODE_CONTEXT 0x03
|
|
0, // CODE_NUL 0x04
|
|
1, // CODE_USE 0x05
|
|
0, // CODE_RETURN 0x06
|
|
0, // CODE_BEEP 0x07
|
|
1, // CODE_DEADKEY 0x08
|
|
-1, // unused 0x09
|
|
2, // CODE_EXTENDED 0x0A
|
|
-1, // CODE_EXTENDEDEND 0x0B (unused)
|
|
1, // CODE_SWITCH 0x0C
|
|
-1, // CODE_KEY 0x0D (never used)
|
|
0, // CODE_CLEARCONTEXT 0x0E
|
|
1, // CODE_CALL 0x0F
|
|
-1, // UC_SENTINEL_EXTENDEDEND 0x10 (not valid with UC_SENTINEL)
|
|
1, // CODE_CONTEXTEX 0x11
|
|
1, // CODE_NOTANY 0x12
|
|
2, // CODE_SETOPT 0x13
|
|
3, // CODE_IFOPT 0x14
|
|
1, // CODE_SAVEOPT 0x15
|
|
1, // CODE_RESETOPT 0x16
|
|
3, // CODE_IFSYSTEMSTORE 0x17
|
|
2 // CODE_SETSYSTEMSTORE 0x18
|
|
};
|
|
|
|
// Ensure that all CODE_### sizes are defined
|
|
static_assert(sizeof(CODE__SIZE) / sizeof(CODE__SIZE[0]) == (CODE_LASTCODE + 1), "Size of array CODE__SIZE not correct");
|
|
|
|
KMX_BOOL KMX_ProcessEvent::LoadKeyboardFromBlob(
|
|
PKMX_BYTE original_buf,
|
|
size_t sz,
|
|
LPKEYBOARD* lpKeyboard
|
|
) {
|
|
LPKEYBOARD kbp;
|
|
PKMX_BYTE buf;
|
|
PKMX_BYTE filebase;
|
|
|
|
|
|
if (!lpKeyboard || !original_buf) {
|
|
DebugLog("Invalid parameter");
|
|
return FALSE;
|
|
}
|
|
|
|
*lpKeyboard = NULL;
|
|
|
|
#ifdef KMX_64BIT
|
|
// allocate enough memory for expanded data structure + original data.
|
|
// Expanded data structure is double the size of data on disk (8-byte
|
|
// pointers) - on disk the "pointers" are relative to the beginning of
|
|
// the file.
|
|
// We save the original data at the end of buf; we don't copy strings, so
|
|
// those will remain in the location at the end of the buffer.
|
|
buf = new KMX_BYTE[sz * 3];
|
|
#else
|
|
buf = new KMX_BYTE[sz];
|
|
#endif
|
|
|
|
if (!buf) {
|
|
DebugLog("Not allocmem");
|
|
return FALSE;
|
|
}
|
|
#ifdef KMX_64BIT
|
|
filebase = buf + sz * 2;
|
|
#else
|
|
filebase = buf;
|
|
#endif
|
|
memcpy(filebase, original_buf, sz);
|
|
|
|
if (*PKMX_DWORD(filebase) != KMX_DWORD(FILEID_COMPILED)) {
|
|
DebugLog("Invalid keyboard - signature is invalid");
|
|
delete[] buf;
|
|
return FALSE;
|
|
}
|
|
|
|
if (!VerifyKeyboard(filebase, sz)) {
|
|
DebugLog("Verify keyboard failed");
|
|
delete[] buf;
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef KMX_64BIT
|
|
kbp = CopyKeyboard(buf, filebase);
|
|
#else
|
|
kbp = FixupKeyboard(buf, filebase);
|
|
#endif
|
|
|
|
if (!kbp) {
|
|
DebugLog("Can't copy/fixup keyboard");
|
|
delete[] buf;
|
|
return FALSE;
|
|
}
|
|
|
|
if (kbp->dwIdentifier != FILEID_COMPILED) {
|
|
DebugLog("errNotFileID");
|
|
delete[] buf;
|
|
return FALSE;
|
|
}
|
|
|
|
*lpKeyboard = kbp;
|
|
return TRUE;
|
|
}
|
|
|
|
PKMX_WCHAR KMX_ProcessEvent::StringOffset(PKMX_BYTE base, KMX_DWORD offset)
|
|
{
|
|
if(offset == 0)
|
|
return NULL;
|
|
return (PKMX_WCHAR)(base + offset);
|
|
}
|
|
|
|
#ifdef KMX_64BIT
|
|
/**
|
|
CopyKeyboard will copy the data read into bufp from x86-sized structures into
|
|
x64-sized structures starting at `base`
|
|
* After this function finishes, we still need to keep the original data because
|
|
we don't copy the strings
|
|
This method is used on 64-bit architectures.
|
|
*/
|
|
LPKEYBOARD KMX_ProcessEvent::CopyKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
|
|
{
|
|
PCOMP_KEYBOARD ckbp = (PCOMP_KEYBOARD) base;
|
|
|
|
/* Copy keyboard structure */
|
|
|
|
LPKEYBOARD kbp = (LPKEYBOARD) bufp;
|
|
bufp += sizeof(KEYBOARD);
|
|
|
|
kbp->dwIdentifier = ckbp->dwIdentifier;
|
|
kbp->dwFileVersion = ckbp->dwFileVersion;
|
|
kbp->dwCheckSum = ckbp->dwCheckSum;
|
|
kbp->xxkbdlayout = ckbp->KeyboardID;
|
|
kbp->IsRegistered = ckbp->IsRegistered;
|
|
kbp->version = ckbp->version;
|
|
kbp->cxStoreArray = ckbp->cxStoreArray;
|
|
kbp->cxGroupArray = ckbp->cxGroupArray;
|
|
kbp->StartGroup[0] = ckbp->StartGroup[0];
|
|
kbp->StartGroup[1] = ckbp->StartGroup[1];
|
|
kbp->dwFlags = ckbp->dwFlags;
|
|
kbp->dwHotKey = ckbp->dwHotKey;
|
|
|
|
kbp->dpStoreArray = (LPSTORE) bufp;
|
|
bufp += sizeof(STORE) * kbp->cxStoreArray;
|
|
|
|
kbp->dpGroupArray = (LPGROUP) bufp;
|
|
bufp += sizeof(GROUP) * kbp->cxGroupArray;
|
|
|
|
PCOMP_STORE csp;
|
|
LPSTORE sp;
|
|
KMX_DWORD i;
|
|
|
|
for(
|
|
csp = (PCOMP_STORE)(base + ckbp->dpStoreArray), sp = kbp->dpStoreArray, i = 0;
|
|
i < kbp->cxStoreArray;
|
|
i++, sp++, csp++)
|
|
{
|
|
sp->dwSystemID = csp->dwSystemID;
|
|
sp->dpName = StringOffset(base, csp->dpName);
|
|
sp->dpString = StringOffset(base, csp->dpString);
|
|
}
|
|
|
|
PCOMP_GROUP cgp;
|
|
LPGROUP gp;
|
|
|
|
for(
|
|
cgp = (PCOMP_GROUP)(base + ckbp->dpGroupArray), gp = kbp->dpGroupArray, i = 0;
|
|
i < kbp->cxGroupArray;
|
|
i++, gp++, cgp++)
|
|
{
|
|
gp->dpName = StringOffset(base, cgp->dpName);
|
|
gp->dpKeyArray = cgp->cxKeyArray > 0 ? (LPKEY) bufp : NULL;
|
|
gp->cxKeyArray = cgp->cxKeyArray;
|
|
bufp += sizeof(KEY) * gp->cxKeyArray;
|
|
gp->dpMatch = StringOffset(base, cgp->dpMatch);
|
|
gp->dpNoMatch = StringOffset(base, cgp->dpNoMatch);
|
|
gp->fUsingKeys = cgp->fUsingKeys;
|
|
|
|
PCOMP_KEY ckp;
|
|
LPKEY kp;
|
|
KMX_DWORD j;
|
|
|
|
for(
|
|
ckp = (PCOMP_KEY)(base + cgp->dpKeyArray), kp = gp->dpKeyArray, j = 0;
|
|
j < gp->cxKeyArray;
|
|
j++, kp++, ckp++)
|
|
{
|
|
kp->Key = ckp->Key;
|
|
kp->Line = ckp->Line;
|
|
kp->ShiftFlags = ckp->ShiftFlags;
|
|
kp->dpOutput = StringOffset(base, ckp->dpOutput);
|
|
kp->dpContext = StringOffset(base, ckp->dpContext);
|
|
}
|
|
}
|
|
|
|
return kbp;
|
|
}
|
|
|
|
#else
|
|
/**
|
|
Fixup the keyboard by expanding pointers. On disk the pointers are stored relative to the
|
|
beginning of the file, but we need real pointers. This method is used on 32-bit architectures.
|
|
*/
|
|
LPKEYBOARD KMX_ProcessEvent::FixupKeyboard(PKMX_BYTE bufp, PKMX_BYTE base)
|
|
{
|
|
KMX_DWORD i, j;
|
|
PCOMP_KEYBOARD ckbp = (PCOMP_KEYBOARD) base;
|
|
PCOMP_GROUP cgp;
|
|
PCOMP_STORE csp;
|
|
PCOMP_KEY ckp;
|
|
LPKEYBOARD kbp = (LPKEYBOARD) bufp;
|
|
LPSTORE sp;
|
|
LPGROUP gp;
|
|
LPKEY kp;
|
|
|
|
kbp->dpStoreArray = (LPSTORE) (base + ckbp->dpStoreArray);
|
|
kbp->dpGroupArray = (LPGROUP) (base + ckbp->dpGroupArray);
|
|
|
|
for(sp = kbp->dpStoreArray, csp = (PCOMP_STORE) sp, i = 0; i < kbp->cxStoreArray; i++, sp++, csp++)
|
|
{
|
|
sp->dpName = StringOffset(base, csp->dpName);
|
|
sp->dpString = StringOffset(base, csp->dpString);
|
|
}
|
|
|
|
for(gp = kbp->dpGroupArray, cgp = (PCOMP_GROUP) gp, i = 0; i < kbp->cxGroupArray; i++, gp++, cgp++)
|
|
{
|
|
gp->dpName = StringOffset(base, cgp->dpName);
|
|
gp->dpKeyArray = cgp->cxKeyArray > 0 ? (LPKEY) (base + cgp->dpKeyArray) : NULL;
|
|
gp->dpMatch = StringOffset(base, cgp->dpMatch);
|
|
gp->dpNoMatch = StringOffset(base, cgp->dpNoMatch);
|
|
|
|
for(kp = gp->dpKeyArray, ckp = (PCOMP_KEY) kp, j = 0; j < gp->cxKeyArray; j++, kp++, ckp++)
|
|
{
|
|
kp->dpOutput = StringOffset(base, ckp->dpOutput);
|
|
kp->dpContext = StringOffset(base, ckp->dpContext);
|
|
}
|
|
}
|
|
|
|
return kbp;
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
KMX_BOOL KMX_ProcessEvent::VerifyKeyboard(PKMX_BYTE filebase, size_t sz)
|
|
{
|
|
KMX_FileValidator *ckbp = reinterpret_cast<KMX_FileValidator*>(filebase);
|
|
|
|
return ckbp->VerifyKeyboard(sz);
|
|
}
|
|
|
|
|
|
KMX_BOOL KMX_FileValidator::VerifyKeyboard(std::size_t _kmn_unused(sz)) const
|
|
{
|
|
KMX_DWORD i;
|
|
PCOMP_STORE csp;
|
|
const PKMX_BYTE filebase = (KMX_BYTE*)this;
|
|
|
|
/* Check file version */
|
|
|
|
if(dwFileVersion < VERSION_MIN ||
|
|
dwFileVersion > VERSION_MAX)
|
|
{
|
|
/* Old or new version -- identify the desired program version */
|
|
for(csp = (PCOMP_STORE)(filebase + dpStoreArray), i = 0; i < cxStoreArray; i++, csp++) {
|
|
if(csp->dwSystemID == TSS_COMPILEDVERSION)
|
|
{
|
|
if(csp->dpString == 0)
|
|
DebugLog("errWrongFileVersion:NULL");
|
|
else
|
|
DebugLog("errWrongFileVersion:%10.10ls", KMX_ProcessEvent::StringOffset(filebase, csp->dpString));
|
|
return FALSE;
|
|
}
|
|
}
|
|
DebugLog("errWrongFileVersion");
|
|
return FALSE;
|
|
}
|
|
|
|
// Verify file structure
|
|
|
|
if(StartGroup[0] != 0xFFFFFFFF && StartGroup[0] >= cxGroupArray) {
|
|
DebugLog("Invalid ANSI start group index");
|
|
return FALSE;
|
|
}
|
|
if(StartGroup[1] != 0xFFFFFFFF && StartGroup[1] >= cxGroupArray) {
|
|
DebugLog("Invalid Unicode start group index");
|
|
return FALSE;
|
|
}
|
|
|
|
// TODO: verify many other offsets such as stores, groups, keys, strings!
|
|
|
|
return TRUE;
|
|
}
|