diff --git a/core/include/ldml/keyboardprocessor_ldml.h b/core/include/ldml/keyboardprocessor_ldml.h index 932b2c4c82..dd77344ce8 100644 --- a/core/include/ldml/keyboardprocessor_ldml.h +++ b/core/include/ldml/keyboardprocessor_ldml.h @@ -21,7 +21,7 @@ #define LDML_LENGTH_KEYS_ITEM 0x10 #define LDML_LENGTH_LOCA 0x10 #define LDML_LENGTH_LOCA_ITEM 0x4 -#define LDML_LENGTH_META 0x24 +#define LDML_LENGTH_META 0x28 #define LDML_LENGTH_SECT 0x10 #define LDML_LENGTH_SECT_ITEM 0x8 #define LDML_LENGTH_STRS 0x10 diff --git a/core/include/ldml/keyboardprocessor_ldml.ts b/core/include/ldml/keyboardprocessor_ldml.ts index ec717481c7..446c191372 100644 --- a/core/include/ldml/keyboardprocessor_ldml.ts +++ b/core/include/ldml/keyboardprocessor_ldml.ts @@ -80,7 +80,7 @@ export const constants: Constants = { /** * length of the 'meta' section */ - length_meta: 36, + length_meta: 40, /** * bitwise or value for fallback=omit in meta.settings */ diff --git a/core/src/kmx/kmx_plus.h b/core/src/kmx/kmx_plus.h index 3510beeb67..312157e0ec 100644 --- a/core/src/kmx/kmx_plus.h +++ b/core/src/kmx/kmx_plus.h @@ -34,6 +34,11 @@ namespace kmx { #pragma warning ( disable : 4200 ) #endif +/** + * Indicates an offset into the strs table (0 = zero length) + */ +typedef KMX_DWORD KMXPLUS_STR; + struct COMP_KMXPLUS_HEADER { KMX_DWORD ident; // 0000 Section name KMX_DWORD size; // 0004 Section length @@ -105,12 +110,13 @@ static_assert(sizeof(struct COMP_KMXPLUS_STRS) == LDML_LENGTH_STRS, "mismatched struct COMP_KMXPLUS_META { static const KMX_DWORD IDENT = LDML_SECTIONID_META; COMP_KMXPLUS_HEADER header; - KMX_DWORD name; - KMX_DWORD author; - KMX_DWORD conform; - KMX_DWORD layout; - KMX_DWORD normalization; - KMX_DWORD indicator; + KMXPLUS_STR name; + KMXPLUS_STR author; + KMXPLUS_STR conform; + KMXPLUS_STR layout; + KMXPLUS_STR normalization; + KMXPLUS_STR indicator; + KMXPLUS_STR version; KMX_DWORD settings; /** * @brief True if section is valid. @@ -121,7 +127,7 @@ struct COMP_KMXPLUS_META { static_assert(sizeof(struct COMP_KMXPLUS_META) == LDML_LENGTH_META, "mismatched size of section meta"); struct COMP_KMXPLUS_LOCA_ENTRY { - KMX_DWORD locale; // 0010+ locale string entry + KMXPLUS_STR locale; // 0010+ locale string entry }; struct COMP_KMXPLUS_LOCA { @@ -142,7 +148,7 @@ static_assert(sizeof(struct COMP_KMXPLUS_LOCA) == LDML_LENGTH_LOCA, "mismatched struct COMP_KMXPLUS_KEYS_ENTRY { KMX_DWORD vkey; KMX_DWORD mod; - KMX_DWORD to; + KMX_DWORD to; // to may be KMXPLUS_STR or UTF32 char KMX_DWORD flags; }; diff --git a/developer/src/kmldmlc/package.json b/developer/src/kmldmlc/package.json index 3b66eb82d3..ee9392e9c9 100644 --- a/developer/src/kmldmlc/package.json +++ b/developer/src/kmldmlc/package.json @@ -23,6 +23,7 @@ "commander": "^3.0.0", "crc-32": "^1.2.2", "restructure": "^3.0.0", + "semver": "^7.3.7", "typescript": "^4.5.4", "xml2js": "^0.4.19" }, @@ -39,8 +40,9 @@ }, "mocha": { "spec": "build/test/**/test-*.js", - "require": ["source-map-support/register"] - + "require": [ + "source-map-support/register" + ] }, "repository": { "type": "git", diff --git a/developer/src/kmldmlc/src/keyman/compiler/meta.ts b/developer/src/kmldmlc/src/keyman/compiler/meta.ts index 383b0c1403..4f7cbe838a 100644 --- a/developer/src/kmldmlc/src/keyman/compiler/meta.ts +++ b/developer/src/kmldmlc/src/keyman/compiler/meta.ts @@ -3,6 +3,7 @@ import { KeyboardSettings, Meta, Meta_NormalizationForm } from "../kmx/kmx-plus" import { isValidEnumValue } from "../util/util"; import { CompilerErrors } from "./errors"; import { SectionCompiler } from "./section-compiler"; +import * as semver from "semver"; export class MetaCompiler extends SectionCompiler { @@ -13,17 +14,33 @@ export class MetaCompiler extends SectionCompiler { public validate(): boolean { let valid = true; - const normalization = this.keyboard.info?.normalization; - if(normalization !== undefined) { - if(!isValidEnumValue(Meta_NormalizationForm, normalization)) { - this.callbacks.reportMessage(CompilerErrors.InvalidNormalization({form: normalization})); - valid = false; - } - } + valid &&= this.validateNormalization(this.keyboard.info?.normalization); + valid &&= this.validateVersion(this.keyboard.version?.number); return valid; } + private validateVersion(versionNumber?: string) { + if(versionNumber !== undefined) { + if(versionNumber.match(/^[=v]/i)) { + // semver ignores a preceding '=' or 'v' + return false; + } + return !!semver.parse(versionNumber, {loose: false, includePrerelease: true}); + } + return true; + } + + private validateNormalization(normalization?: string) { + if (normalization !== undefined) { + if (!isValidEnumValue(Meta_NormalizationForm, normalization)) { + this.callbacks.reportMessage(CompilerErrors.InvalidNormalization({ form: normalization })); + return false; + } + } + return true; + } + public compile(): Meta { let result = new Meta(); result.name = this.keyboard.names?.name?.[0]?.value; @@ -32,6 +49,7 @@ export class MetaCompiler extends SectionCompiler { result.layout = this.keyboard.info?.layout; result.normalization = this.keyboard.info?.normalization as Meta_NormalizationForm; result.indicator = this.keyboard.info?.indicator; + result.version = this.keyboard.version?.number ?? "0"; result.settings = (this.keyboard.settings?.fallback == "omit" ? KeyboardSettings.fallback : 0) | (this.keyboard.settings?.transformFailure == "omit" ? KeyboardSettings.transformFailure : 0) | diff --git a/developer/src/kmldmlc/src/keyman/kmx/kmx-plus-builder.ts b/developer/src/kmldmlc/src/keyman/kmx/kmx-plus-builder.ts index 4f73ef6a65..287ad7f0e3 100644 --- a/developer/src/kmldmlc/src/keyman/kmx/kmx-plus-builder.ts +++ b/developer/src/kmldmlc/src/keyman/kmx/kmx-plus-builder.ts @@ -41,6 +41,7 @@ interface BUILDER_META extends BUILDER_SECTION { layout: number; //str normalization: number; //str indicator: number; //str + version: number; //str settings: number; //bitfield }; @@ -100,7 +101,7 @@ export default class KMXPlusBuilder { } } - let idx = this.sect_strs.items.findIndex(v => v._value == value); + let idx = this.sect_strs.items.findIndex(v => v._value === value); if(idx >= 0) { return idx; } @@ -147,6 +148,7 @@ export default class KMXPlusBuilder { layout: this.alloc_string(this.file.kmxplus.meta.layout), normalization: this.alloc_string(this.file.kmxplus.meta.normalization), indicator: this.alloc_string(this.file.kmxplus.meta.indicator), + version: this.alloc_string(this.file.kmxplus.meta.version), settings: this.file.kmxplus.meta.settings ?? 0, }; } diff --git a/developer/src/kmldmlc/src/keyman/kmx/kmx-plus.ts b/developer/src/kmldmlc/src/keyman/kmx/kmx-plus.ts index dad532b651..7d53b6261e 100644 --- a/developer/src/kmldmlc/src/keyman/kmx/kmx-plus.ts +++ b/developer/src/kmldmlc/src/keyman/kmx/kmx-plus.ts @@ -25,6 +25,7 @@ export class Meta extends Section { layout: string; normalization: Meta_NormalizationForm; indicator: string; + version: string; // semver version string, defaults to "0" settings: KeyboardSettings; }; @@ -133,6 +134,7 @@ export default class KMXPlusFile extends KMXFile { layout: r.uint32le, //str normalization: r.uint32le, //str indicator: r.uint32le, //str + version: r.uint32le, //str settings: r.uint32le, //new r.Bitfield(r.uint32le, ['fallback', 'transformFailure', 'transformPartial']) }); diff --git a/developer/src/kmldmlc/src/keyman/ldml-keyboard/ldml-keyboard-xml.ts b/developer/src/kmldmlc/src/keyman/ldml-keyboard/ldml-keyboard-xml.ts index c6315bd5b4..ee812447d4 100644 --- a/developer/src/kmldmlc/src/keyman/ldml-keyboard/ldml-keyboard-xml.ts +++ b/developer/src/kmldmlc/src/keyman/ldml-keyboard/ldml-keyboard-xml.ts @@ -19,6 +19,7 @@ export interface LKKeyboard { conformsTo?: string; locales?: LKLocales; + version?: LKVersion; info?: LKInfo; names?: LKNames; settings?: LKSettings; @@ -34,6 +35,10 @@ export interface LKLocale { id?: string; }; +export interface LKVersion { + number: string; // semver string +} + export interface LKInfo { author?: string; indicator?: string; diff --git a/developer/src/kmldmlc/test/fixtures/basic.txt b/developer/src/kmldmlc/test/fixtures/basic.txt index 38171949b9..d8a6213fdb 100644 --- a/developer/src/kmldmlc/test/fixtures/basic.txt +++ b/developer/src/kmldmlc/test/fixtures/basic.txt @@ -3,7 +3,7 @@ block(kmxheader) # struct COMP_KEYBOARD { 00 10 00 00 # KMX_DWORD dwFileVersion; // 0004 Version of the file - Keyman 4.0 is 0x0400 - fc 29 af f8 # KMX_DWORD dwCheckSum; // 0008 As stored in keyboard + 13 06 7c 19 # KMX_DWORD dwCheckSum; // 0008 As stored in keyboard 00 00 00 00 # KMX_DWORD KeyboardID; // 000C as stored in HKEY_LOCAL_MACHINE//system//currentcontrolset//control//keyboard layouts 01 00 00 00 # KMX_DWORD IsRegistered; // 0010 00 00 00 00 # KMX_DWORD version; // 0014 keyboard version @@ -53,7 +53,7 @@ block(sect) # struct COMP_KMXPLUS_SECT { block(strs) # struct COMP_KMXPLUS_STRS { 73 74 72 73 # KMX_DWORD header.ident; // 0000 Section name - strs diff(strs,endstrs) # KMX_DWORD header.size; // 0004 Section length - 09 00 00 00 # KMX_DWORD count; // 0008 count of str entries + 0A 00 00 00 # KMX_DWORD count; // 0008 count of str entries 00 00 00 00 # KMX_DWORD reserved; // 000C padding # Next sections are string entries @@ -66,6 +66,7 @@ block(strs) # struct COMP_KMXPLUS_STRS { diff(strs,strLayout) sizeof(strLayout,2) diff(strs,strNorm) sizeof(strNorm,2) diff(strs,strIndicator) sizeof(strIndicator,2) + diff(strs,strVersion) sizeof(strVersion,2) diff(strs,strLocale) sizeof(strLocale,2) diff(strs,strKey1) sizeof(strKey1,2) diff(strs,strKey2) sizeof(strKey2,2) @@ -81,6 +82,7 @@ block(strConformsTo) 74 00 65 00 63 00 68 00 70 00 72 00 65 00 76 00 block(strLayout) 71 00 77 00 65 00 72 00 74 00 79 00 block(x) 00 00 # 3:qwerty block(strNorm) 4e 00 46 00 43 00 block(x) 00 00 # 4:NFC block(strIndicator) 3d d8 40 de block(x) 00 00 # 5:🙀 +block(strVersion) 30 00 block(x) 00 00 # 6:0 block(strLocale) 6d 00 74 00 block(x) 00 00 # 7:mt block(strKey1) 27 01 block(x) 00 00 # 8:ħ block(strKey2) 90 17 b6 17 block(x) 00 00 # 9:ថា @@ -96,6 +98,7 @@ block(meta) # struct COMP_KMXPLUS_META { 03 00 00 00 # KMX_DWORD layout; 04 00 00 00 # KMX_DWORD normalization; 05 00 00 00 # KMX_DWORD indicator; + 06 00 00 00 # KMX_DWORD version; 00 00 00 00 # KMX_DWORD settings; # }; @@ -104,7 +107,7 @@ block(loca) # struct COMP_KMXPLUS_LOCA { sizeof(loca) # KMX_DWORD header.size; // 0004 Section length 01 00 00 00 # KMX_DWORD count; // 0008 number of locales 00 00 00 00 # KMX_DWORD reserved; - 06 00 00 00 # KMX_DWORD locale; // 0010+ locale string entry = 'mt' + 07 00 00 00 # KMX_DWORD locale; // 0010+ locale string entry = 'mt' # }; block(keys) # struct COMP_KMXPLUS_KEYS { @@ -116,7 +119,7 @@ block(keys) # struct COMP_KMXPLUS_KEYS { # Keys data: - c0 00 00 00 00 00 00 00 07 00 00 00 01 00 00 00 # KMX_DWORD vkey, mod, to, flags; - 31 00 00 00 00 00 00 00 08 00 00 00 01 00 00 00 # KMX_DWORD vkey, mod, to, flags; + c0 00 00 00 00 00 00 00 08 00 00 00 01 00 00 00 # KMX_DWORD vkey, mod, to, flags; + 31 00 00 00 00 00 00 00 09 00 00 00 01 00 00 00 # KMX_DWORD vkey, mod, to, flags; block(eof) # end of file \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5ad4edfda9..529be524d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -477,6 +477,7 @@ "commander": "^3.0.0", "crc-32": "^1.2.2", "restructure": "^3.0.0", + "semver": "^7.3.7", "typescript": "^4.5.4", "xml2js": "^0.4.19" }, @@ -4561,12 +4562,14 @@ "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" }, "node_modules/lru-cache": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz", - "integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==", - "devOptional": true, + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": ">=12" + "node": ">=10" } }, "node_modules/macos-release": { @@ -5867,18 +5870,17 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "node_modules/semver": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz", - "integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==", - "devOptional": true, + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dependencies": { - "lru-cache": "^7.4.0" + "lru-cache": "^6.0.0" }, "bin": { "semver": "bin/semver.js" }, "engines": { - "node": "^10.0.0 || ^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=10" } }, "node_modules/send": { @@ -6884,8 +6886,7 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "optional": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { "version": "1.10.2", @@ -7814,6 +7815,7 @@ "crc-32": "^1.2.2", "mocha": "^8.4.0", "restructure": "^3.0.0", + "semver": "*", "ts-node": "^9.1.1", "typescript": "^4.5.4", "xml2js": "^0.4.19" @@ -11003,10 +11005,12 @@ "integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0=" }, "lru-cache": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.8.1.tgz", - "integrity": "sha512-E1v547OCgJvbvevfjgK9sNKIVXO96NnsTsFPBlg4ZxjhsJSODoH9lk8Bm0OxvHNm6Vm5Yqkl/1fErDxhYL8Skg==", - "devOptional": true + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } }, "macos-release": { "version": "2.5.0", @@ -11986,12 +11990,11 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "semver": { - "version": "7.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.6.tgz", - "integrity": "sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==", - "devOptional": true, + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "requires": { - "lru-cache": "^7.4.0" + "lru-cache": "^6.0.0" } }, "send": { @@ -12756,8 +12759,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "optional": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { "version": "1.10.2",