@sie-js/vkp 1.0.6 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,14 +16,14 @@ import fs from 'fs';
16
16
  import { vkpNormalize, vkpParse } from '@sie-js/vkp';
17
17
 
18
18
  // Convert from windows-1251 to UTF-8 + replace CRLF to LF
19
- let patchText = vkpNormalize(fs.readFileSync('../patches/patches/E71v45/10732-ElfPack-18_03_2024-v3_2_2.vkp'));
19
+ const patchText = vkpNormalize(fs.readFileSync('../patches/patches/E71v45/10732-ElfPack-18_03_2024-v3_2_2.vkp'));
20
20
 
21
21
  // Parse patch
22
- let vkp = vkpParse(patchText);
22
+ const vkp = vkpParse(patchText);
23
23
  console.dir(vkp, { depth: null });
24
24
 
25
25
  if (vkp.warnings.length || vkp.errors.length) {
26
- for (let warn of vkp.warnings) {
26
+ for (const warn of vkp.warnings) {
27
27
  console.log(`Warning: ${warn.message}`);
28
28
  console.log("```");
29
29
  console.log(warn.codeFrame(patchText));
@@ -31,7 +31,7 @@ if (vkp.warnings.length || vkp.errors.length) {
31
31
  console.log("");
32
32
  }
33
33
 
34
- for (let err of vkp.errors) {
34
+ for (const err of vkp.errors) {
35
35
  console.log(`Error: ${err.message}`);
36
36
  console.log("```");
37
37
  console.log(err.codeFrame(patchText));
@@ -0,0 +1,12 @@
1
+ export interface VkpLocation {
2
+ line: number;
3
+ column: number;
4
+ }
5
+ export declare class VkpParseError extends Error {
6
+ loc: VkpLocation;
7
+ hint?: string;
8
+ constructor(message: string, loc: VkpLocation, hint?: string);
9
+ codeFrame(text: string): string;
10
+ }
11
+ export declare function codeFrame(text: string, lineNum: number, colNum: number): string;
12
+ export declare function getLocByOffset(text: string, offset: number): VkpLocation;
@@ -0,0 +1,65 @@
1
+ export class VkpParseError extends Error {
2
+ loc;
3
+ hint;
4
+ constructor(message, loc, hint) {
5
+ super(`${message} at line ${loc.line} col ${loc.column}${hint ? "\n" + hint : ""}`);
6
+ this.name = "VkpParseError";
7
+ this.loc = loc;
8
+ this.hint = hint;
9
+ }
10
+ codeFrame(text) {
11
+ return codeFrame(text, this.loc.line, this.loc.column);
12
+ }
13
+ }
14
+ export function codeFrame(text, lineNum, colNum) {
15
+ const lines = text.split(/\r\n|\n/);
16
+ const maxLineNumLen = lines.length.toString().length + 1;
17
+ let out = "";
18
+ let n = 1;
19
+ for (const line of lines) {
20
+ if (Math.abs(n - lineNum) > 3) {
21
+ n++;
22
+ continue;
23
+ }
24
+ out += `${n == lineNum ? '>' : ' '}${n.toString().padStart(maxLineNumLen, ' ')} | ${tabToSpaces(line)}\n`;
25
+ if (n == lineNum) {
26
+ out += ` ${" ".repeat(maxLineNumLen)} | ${strToSpaces(line.substring(0, colNum - 1))}^\n`;
27
+ }
28
+ n++;
29
+ }
30
+ return out;
31
+ }
32
+ export function getLocByOffset(text, offset) {
33
+ let line = 1;
34
+ let column = 1;
35
+ for (let i = 0; i < text.length; i++) {
36
+ const c = text.charAt(i);
37
+ if (c == "\n") {
38
+ column = 1;
39
+ line++;
40
+ }
41
+ if (i == offset)
42
+ return { line, column };
43
+ }
44
+ return { line, column: 1 };
45
+ }
46
+ function strToSpaces(line) {
47
+ return tabToSpaces(line).replace(/./g, ' ');
48
+ }
49
+ function tabToSpaces(line) {
50
+ let newStr = "";
51
+ let virtualSymbols = 0;
52
+ for (let i = 0; i < line.length; i++) {
53
+ const c = line.charAt(i);
54
+ if (c == "\t") {
55
+ const spacesCnt = 4 - virtualSymbols % 4;
56
+ newStr += " ".repeat(spacesCnt);
57
+ virtualSymbols += spacesCnt;
58
+ }
59
+ else {
60
+ virtualSymbols++;
61
+ newStr += c;
62
+ }
63
+ }
64
+ return newStr;
65
+ }
@@ -0,0 +1,33 @@
1
+ import { VkpPragmaName } from "./parser.js";
2
+ import { VkpLocation, VkpParseError } from "./VkpParseError.js";
3
+ export type VkpContentType = "RTF" | "PATCH" | "DOWNLOAD_STUB" | "EMPTY" | "UNKNOWN";
4
+ export interface VkpWrite {
5
+ addr: number;
6
+ size: number;
7
+ old?: Buffer;
8
+ new: Buffer;
9
+ loc: VkpLocation;
10
+ pragmas: Record<VkpPragmaName, boolean>;
11
+ }
12
+ export interface VkpParseResult {
13
+ valid: boolean;
14
+ writes: VkpWrite[];
15
+ warnings: VkpParseError[];
16
+ errors: VkpParseError[];
17
+ }
18
+ export interface VkpParseOptions {
19
+ allowEmptyOldData?: boolean;
20
+ allowPlaceholders?: boolean;
21
+ }
22
+ export interface VkpOffsetCorrector {
23
+ value: number;
24
+ text: string;
25
+ loc: VkpLocation;
26
+ }
27
+ export declare function vkpParse(text: string, options?: VkpParseOptions): VkpParseResult;
28
+ export declare function vkpDetectContent(text: string): VkpContentType;
29
+ export declare function vkpNormalizeWithRTF(text: Buffer): Promise<string>;
30
+ export declare function vkpNormalize(text: Buffer): string;
31
+ export declare function vkpCanonicalize(text: string): Buffer;
32
+ export * from "./parser.js";
33
+ export * from "./VkpParseError.js";
@@ -0,0 +1,156 @@
1
+ import iconv from "iconv-lite";
2
+ import { vkpRawParser } from "./parser.js";
3
+ import { VkpParseError } from "./VkpParseError.js";
4
+ const DEFAULT_PRAGMAS = {
5
+ warn_no_old_on_apply: true,
6
+ warn_if_new_exist_on_apply: true,
7
+ warn_if_old_exist_on_undo: true,
8
+ undo: true,
9
+ old_equal_ff: false,
10
+ };
11
+ export function vkpParse(text, options) {
12
+ const validOptions = {
13
+ allowEmptyOldData: false,
14
+ allowPlaceholders: false,
15
+ ...options
16
+ };
17
+ const vkp = {
18
+ valid: false,
19
+ writes: [],
20
+ warnings: [],
21
+ errors: [],
22
+ };
23
+ const pragmas = { ...DEFAULT_PRAGMAS };
24
+ const pragmaToLocation = {};
25
+ let offsetCorrector;
26
+ vkpRawParser(text, {
27
+ onPragma(value, loc) {
28
+ const pragmaName = value.pragma.name;
29
+ if (value.pragma.action == "enable") {
30
+ if (pragmas[pragmaName]) {
31
+ vkp.warnings.push(new VkpParseError(`Useless "#pragma ${value.pragma.action} ${pragmaName}" has no effect`, loc, `You can safely remove this line.`));
32
+ }
33
+ else {
34
+ pragmas[pragmaName] = true;
35
+ pragmaToLocation[pragmaName] = loc;
36
+ }
37
+ }
38
+ else if (value.pragma.action == "disable") {
39
+ if (!pragmas[pragmaName]) {
40
+ vkp.warnings.push(new VkpParseError(`Useless "#pragma ${value.pragma.action} ${pragmaName}" has no effect`, loc, `You can safely remove this line.`));
41
+ }
42
+ else {
43
+ pragmaToLocation[pragmaName] = loc;
44
+ pragmas[pragmaName] = false;
45
+ }
46
+ }
47
+ },
48
+ onPatchData(data, loc) {
49
+ let oldData = data.old ? data.old.buffer : undefined;
50
+ const newData = data.new.buffer;
51
+ if (data.new.placeholders > 0) {
52
+ if (!validOptions.allowPlaceholders)
53
+ vkp.errors.push(new VkpParseError(`Found placeholder instead of real patch data`, data.new.loc));
54
+ }
55
+ if (pragmas.old_equal_ff && !oldData)
56
+ oldData = Buffer.alloc(newData.length).fill(0xFF);
57
+ if (oldData && oldData.length < newData.length) {
58
+ vkp.errors.push(new VkpParseError(`Old data (${oldData.length} bytes) is less than new data (${newData.length} bytes)`, data.old.loc));
59
+ }
60
+ if (pragmas.warn_no_old_on_apply && !oldData) {
61
+ if (!validOptions.allowEmptyOldData) {
62
+ vkp.warnings.push(new VkpParseError(`Old data is not specified`, data.new.loc, `Undo operation is impossible!`));
63
+ }
64
+ }
65
+ vkp.writes.push({
66
+ addr: (offsetCorrector ? offsetCorrector.value : 0) + data.address,
67
+ size: newData.length,
68
+ old: oldData,
69
+ new: newData,
70
+ loc,
71
+ pragmas: { ...pragmas }
72
+ });
73
+ },
74
+ onOffset(value, loc) {
75
+ offsetCorrector = { value: value.offset, text: value.text, loc };
76
+ },
77
+ onWarning(e) {
78
+ vkp.warnings.push(e);
79
+ },
80
+ onError(e) {
81
+ vkp.errors.push(e);
82
+ }
83
+ });
84
+ for (const k of Object.keys(pragmas)) {
85
+ if (pragmas[k] !== DEFAULT_PRAGMAS[k]) {
86
+ const cancel = pragmas[k] ? `#pragma disable ${k}` : `#pragma enable ${k}`;
87
+ vkp.warnings.push(new VkpParseError(`Uncanceled pragma "${k}"`, pragmaToLocation[k], `Please put "${cancel}" at the end of the patch.`));
88
+ }
89
+ }
90
+ if (offsetCorrector && offsetCorrector.value != 0) {
91
+ vkp.warnings.push(new VkpParseError(`Uncanceled offset ${offsetCorrector.text}`, offsetCorrector.loc, `Please put "+0" at the end of the patch.`));
92
+ }
93
+ vkp.valid = (vkp.errors.length == 0);
94
+ return vkp;
95
+ }
96
+ export function vkpDetectContent(text) {
97
+ if (text.indexOf('{\\rtf1') >= 0)
98
+ return "RTF";
99
+ const trimmedText = text.replace(/\/\*.*?\*\//gs, '').replace(/(\/\/|;|#).*?$/mg, '');
100
+ if (trimmedText.match(/^\s*(0x[a-f0-9]+|[a-f0-9]+)\s*:[^\\/]/mi))
101
+ return "PATCH";
102
+ if (text.match(/;!(к патчу прикреплён файл|There is a file attached to this patch), https?:\/\//i))
103
+ return "DOWNLOAD_STUB";
104
+ if (!trimmedText.trim().length)
105
+ return "EMPTY";
106
+ return "UNKNOWN";
107
+ }
108
+ // CP1251 -> UTF-8 + CRLF -> LF (with RTF support)
109
+ export async function vkpNormalizeWithRTF(text) {
110
+ if (!Buffer.isBuffer(text))
111
+ throw new Error(`Patch text is not Buffer!`);
112
+ const { default: RTFParser } = await import('rtf-parser');
113
+ if (text.indexOf('{\\rtf1') >= 0) {
114
+ // Strip RTF images
115
+ while (true) {
116
+ const pictureIndex = text.indexOf('{\\pict');
117
+ if (pictureIndex >= 0) {
118
+ const pictureEndIndex = text.indexOf('}', pictureIndex);
119
+ if (pictureIndex >= 0) {
120
+ text = Buffer.concat([text.subarray(0, pictureIndex), text.subarray(pictureEndIndex + 1)]);
121
+ continue;
122
+ }
123
+ }
124
+ break;
125
+ }
126
+ const textStr = text.toString('utf-8').replace(/{\\pict.*?}/gsi, ''); // remove pictures
127
+ const parsed = await new Promise((resolve, reject) => {
128
+ RTFParser.string(textStr, (err, doc) => {
129
+ if (err) {
130
+ reject(err);
131
+ }
132
+ else {
133
+ resolve(doc);
134
+ }
135
+ });
136
+ });
137
+ const lines = [];
138
+ for (const p of parsed.content) {
139
+ lines.push(p.content.map((s) => s.value).join(''));
140
+ }
141
+ return lines.join('\n');
142
+ }
143
+ return iconv.decode(text, 'windows-1251').replace(/(\r\n|\n|\r)/g, "\n");
144
+ }
145
+ // CP1251 -> UTF-8 + CRLF -> LF
146
+ export function vkpNormalize(text) {
147
+ if (!Buffer.isBuffer(text))
148
+ throw new Error(`Patch text is not Buffer!`);
149
+ return iconv.decode(text, 'windows-1251').replace(/(\r\n|\n|\r)/g, "\n");
150
+ }
151
+ // UTF-8 -> CP1251 + LF -> CRLF
152
+ export function vkpCanonicalize(text) {
153
+ return iconv.encode(text.replace(/(\r\n|\n|\r)/g, "\r\n"), 'windows-1251');
154
+ }
155
+ export * from "./parser.js";
156
+ export * from "./VkpParseError.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,381 @@
1
+ import { test, expect } from "vitest";
2
+ import { vkpParse } from "./index.js";
3
+ test('warn: useless pragma', () => {
4
+ const vkp = vkpParse(`#pragma enable warn_no_old_on_apply`);
5
+ expect(vkp.valid).toBe(true);
6
+ expect(vkp.warnings.length).toBe(1);
7
+ expect(vkp.warnings[0]).toHaveProperty("message", "Useless \"#pragma enable warn_no_old_on_apply\" has no effect at line 1 col 1\nYou can safely remove this line.");
8
+ });
9
+ test('warn: uncanceled pragma', () => {
10
+ const vkp = vkpParse(`#pragma disable warn_no_old_on_apply`);
11
+ expect(vkp.valid).toBe(true);
12
+ expect(vkp.warnings.length).toBe(1);
13
+ expect(vkp.warnings[0]).toHaveProperty("message", "Uncanceled pragma \"warn_no_old_on_apply\" at line 1 col 1\nPlease put \"#pragma enable warn_no_old_on_apply\" at the end of the patch.");
14
+ });
15
+ test('warn: uncanceled offset', () => {
16
+ const vkp = vkpParse(`+123`);
17
+ expect(vkp.valid).toBe(true);
18
+ expect(vkp.warnings.length).toBe(1);
19
+ expect(vkp.warnings[0]).toHaveProperty("message", "Uncanceled offset +123 at line 1 col 1\nPlease put \"+0\" at the end of the patch.");
20
+ });
21
+ test('warn: bad comments', () => {
22
+ const vkp = vkpParse(`
23
+ */
24
+ /* comment...
25
+ `);
26
+ expect(vkp.valid).toBe(true);
27
+ expect(vkp.warnings.length).toBe(2);
28
+ expect(vkp.warnings[0]).toHaveProperty("message", "Trailing multiline comment end at line 2 col 3");
29
+ expect(vkp.warnings[1]).toHaveProperty("message", "Unfinished multiline comment at line 3 col 3");
30
+ });
31
+ test('warn: no old data', () => {
32
+ const vkp = vkpParse(`
33
+ AA: BB
34
+ `);
35
+ expect(vkp.valid).toBe(true);
36
+ expect(vkp.warnings.length).toBe(1);
37
+ expect(vkp.warnings[0]).toHaveProperty("message", "Old data is not specified at line 2 col 7\nUndo operation is impossible!");
38
+ });
39
+ test('error: space after number', () => {
40
+ const vkp = vkpParse(`
41
+ AAAA: BB 0i123; comment
42
+ AAAA: BB 0x12; comment
43
+ AAAA: BB CC; comment
44
+ `);
45
+ expect(vkp.valid).toBe(false);
46
+ expect(vkp.warnings.length).toBe(0);
47
+ expect(vkp.errors.length).toBe(2);
48
+ expect(vkp.errors[0]).toHaveProperty("message", "No whitespace between number and comment at line 2 col 17");
49
+ expect(vkp.errors[1]).toHaveProperty("message", "No whitespace between number and comment at line 3 col 16");
50
+ });
51
+ test('error: placeholder', () => {
52
+ const vkp = vkpParse(`AAAA: BB XX`);
53
+ expect(vkp.valid).toBe(false);
54
+ expect(vkp.warnings.length).toBe(0);
55
+ expect(vkp.errors.length).toBe(1);
56
+ expect(vkp.errors[0]).toHaveProperty("message", "Found placeholder instead of real patch data at line 1 col 10");
57
+ });
58
+ test('error: invalid hex data', () => {
59
+ const vkp = vkpParse(`AAAA: BB B`);
60
+ expect(vkp.valid).toBe(false);
61
+ expect(vkp.warnings.length).toBe(0);
62
+ expect(vkp.errors.length).toBe(1);
63
+ expect(vkp.errors[0]).toHaveProperty("message", "Hex data (B) must be even length at line 1 col 10");
64
+ });
65
+ test('error: old data is less than new data', () => {
66
+ const vkp = vkpParse(`AAAA: BB BBCC`);
67
+ expect(vkp.valid).toBe(false);
68
+ expect(vkp.warnings.length).toBe(0);
69
+ expect(vkp.errors.length).toBe(1);
70
+ expect(vkp.errors[0]).toHaveProperty("message", "Old data (1 bytes) is less than new data (2 bytes) at line 1 col 7");
71
+ });
72
+ test('error: comment tokens in string', () => {
73
+ const vkp = vkpParse(`
74
+ AAAA: AABBCCDDEE "//"
75
+ AAAA: AABBCCDDEE "/*"
76
+ AAAA: AABBCCDDEE "\\/\\/"
77
+ AAAA: AABBCCDDEE "\\/\\*"
78
+ `);
79
+ expect(vkp.valid).toBe(false);
80
+ expect(vkp.warnings.length).toBe(0);
81
+ expect(vkp.errors.length).toBe(2);
82
+ expect(vkp.errors[0]).toHaveProperty("message", "Unescaped // is not allowed in string: \"//\" at line 2 col 20\nEscape these ambiguous characters like this: \\/* or \\/\\/.");
83
+ expect(vkp.errors[1]).toHaveProperty("message", "Unescaped /* is not allowed in string: \"/*\" at line 3 col 20\nEscape these ambiguous characters like this: \\/* or \\/\\/.");
84
+ });
85
+ test('error: number ranges', () => {
86
+ const vkp = vkpParse(`
87
+ AAAA: AABBCCDDEE 0i0,0i999
88
+ AAAA: AABBCCDDEE 0i0,0i99999
89
+ AAAA: AABBCCDDEE 0i0,0i99999999
90
+ AAAA: AABBCCDDEE 0i0,0i9999999999
91
+ AAAA: AABBCCDDEE 0i0,0i9999999999999
92
+ AAAA: AABBCCDDEE 0i0,0i999999999999999
93
+ AAAA: AABBCCDDEE 0i0,0i99999999999999999
94
+ AAAA: AABBCCDDEE 0i0,0i99999999999999999999
95
+
96
+ AAAA: AABBCCDDEE 0i0,0i-999
97
+ AAAA: AABBCCDDEE 0i0,0i-99999
98
+ AAAA: AABBCCDDEE 0i0,0i-99999999
99
+ AAAA: AABBCCDDEE 0i0,0i-9999999999
100
+ AAAA: AABBCCDDEE 0i0,0i-9999999999999
101
+ AAAA: AABBCCDDEE 0i0,0i-999999999999999
102
+ AAAA: AABBCCDDEE 0i0,0i-99999999999999999
103
+ AAAA: AABBCCDDEE 0i0,0i-99999999999999999999
104
+ `);
105
+ expect(vkp.valid).toBe(false);
106
+ expect(vkp.warnings.length).toBe(0);
107
+ expect(vkp.errors.length).toBe(16);
108
+ expect(vkp.errors[0]).toHaveProperty("message", "Number 0i999 exceeds allowed range 0 ... 255 at line 2 col 24");
109
+ expect(vkp.errors[1]).toHaveProperty("message", "Number 0i99999 exceeds allowed range 0 ... 65535 at line 3 col 24");
110
+ expect(vkp.errors[2]).toHaveProperty("message", "Number 0i99999999 exceeds allowed range 0 ... 16777215 at line 4 col 24");
111
+ expect(vkp.errors[3]).toHaveProperty("message", "Number 0i9999999999 exceeds allowed range 0 ... 4294967295 at line 5 col 24");
112
+ expect(vkp.errors[4]).toHaveProperty("message", "Number 0i9999999999999 exceeds allowed range 0 ... 1099511627775 at line 6 col 24");
113
+ expect(vkp.errors[5]).toHaveProperty("message", "Number 0i999999999999999 exceeds allowed range 0 ... 281474976710655 at line 7 col 24");
114
+ expect(vkp.errors[6]).toHaveProperty("message", "Number 0i99999999999999999 exceeds allowed range 0 ... 72057594037927935 at line 8 col 24");
115
+ expect(vkp.errors[7]).toHaveProperty("message", "Number 0i99999999999999999999 exceeds allowed range 0 ... 18446744073709551615 at line 9 col 24");
116
+ expect(vkp.errors[8]).toHaveProperty("message", "Number 0i-999 exceeds allowed range -127 ... +127 at line 11 col 24");
117
+ expect(vkp.errors[9]).toHaveProperty("message", "Number 0i-99999 exceeds allowed range -32767 ... +32767 at line 12 col 24");
118
+ expect(vkp.errors[10]).toHaveProperty("message", "Number 0i-99999999 exceeds allowed range -8388607 ... +8388607 at line 13 col 24");
119
+ expect(vkp.errors[11]).toHaveProperty("message", "Number 0i-9999999999 exceeds allowed range -2147483647 ... +2147483647 at line 14 col 24");
120
+ expect(vkp.errors[12]).toHaveProperty("message", "Number 0i-9999999999999 exceeds allowed range -549755813887 ... +549755813887 at line 15 col 24");
121
+ expect(vkp.errors[13]).toHaveProperty("message", "Number 0i-999999999999999 exceeds allowed range -140737488355327 ... +140737488355327 at line 16 col 24");
122
+ expect(vkp.errors[14]).toHaveProperty("message", "Number 0i-99999999999999999 exceeds allowed range -36028797018963967 ... +36028797018963967 at line 17 col 24");
123
+ expect(vkp.errors[15]).toHaveProperty("message", "Number 0i-99999999999999999999 exceeds allowed range -9223372036854775807 ... +9223372036854775807 at line 18 col 24");
124
+ });
125
+ test('error: bad numbers', () => {
126
+ const vkp = vkpParse(`
127
+ AAAA: AABBCCDDEE 0i0,0n1234
128
+ AAAA: AABBCCDDEE 0i0,0n111111111111111111111111111111111
129
+ `);
130
+ expect(vkp.valid).toBe(false);
131
+ expect(vkp.warnings.length).toBe(0);
132
+ expect(vkp.errors.length).toBe(2);
133
+ expect(vkp.errors[0]).toHaveProperty("message", "Syntax error at line 2 col 24");
134
+ expect(vkp.errors[1]).toHaveProperty("message", "Number 0n111111111111111111111111111111111 exceeds allowed range 0n0 ... 0n11111111111111111111111111111111 at line 3 col 24");
135
+ });
136
+ test('error: bad decimal numbers', () => {
137
+ const vkp = vkpParse(`
138
+ 00000000: FF,FF,FF 0i+000,0i+00,0i+0
139
+ 00000000: FFFF 0i+0000
140
+ 00000000: FFFFFF 0i+0000000
141
+ 00000000: FFFFFFFF 0i+000000000
142
+ 00000000: FFFFFFFFFF 0i+000000000000
143
+ 00000000: FFFFFFFFFFFF 0i+00000000000000
144
+ 00000000: FFFFFFFFFFFFFF 0i+0000000000000000
145
+ 00000000: FFFFFFFFFFFFFFFF 0i+0000000000000000000
146
+ `);
147
+ expect(vkp.valid).toBe(false);
148
+ expect(vkp.warnings.length).toBe(0);
149
+ expect(vkp.errors.length).toBe(7);
150
+ const hint = "Must be: 3 (for BYTE), 5 (for WORD), 8 (for 3 BYTES), 10 (for DWORD), 13 (for 5 BYTES), 15 (for 6 BYTES), 17 (for 7 BYTES), " +
151
+ "20 (for 8 BYTES).Use leading zeroes to match the number of digits.";
152
+ expect(vkp.errors[0]).toHaveProperty("message", "The wrong number of digits in integer (0i+0000) at line 3 col 18\n" + hint);
153
+ expect(vkp.errors[1]).toHaveProperty("message", "The wrong number of digits in integer (0i+0000000) at line 4 col 20\n" + hint);
154
+ expect(vkp.errors[2]).toHaveProperty("message", "The wrong number of digits in integer (0i+000000000) at line 5 col 22\n" + hint);
155
+ expect(vkp.errors[3]).toHaveProperty("message", "The wrong number of digits in integer (0i+000000000000) at line 6 col 24\n" + hint);
156
+ expect(vkp.errors[4]).toHaveProperty("message", "The wrong number of digits in integer (0i+00000000000000) at line 7 col 26\n" + hint);
157
+ expect(vkp.errors[5]).toHaveProperty("message", "The wrong number of digits in integer (0i+0000000000000000) at line 8 col 28\n" + hint);
158
+ expect(vkp.errors[6]).toHaveProperty("message", "The wrong number of digits in integer (0i+0000000000000000000) at line 9 col 30\n" + hint);
159
+ });
160
+ test('error: bad address & offset', () => {
161
+ const vkp = vkpParse(`
162
+ +AAAAAAAAA
163
+ AAAAAAAAA: AA BB
164
+ `);
165
+ expect(vkp.valid).toBe(false);
166
+ expect(vkp.warnings.length).toBe(0);
167
+ expect(vkp.errors.length).toBe(2);
168
+ expect(vkp.errors[0]).toHaveProperty("message", "Offset +AAAAAAAAA exceeds allowed range 00000000 ... FFFFFFFF at line 2 col 3");
169
+ expect(vkp.errors[1]).toHaveProperty("message", "Address AAAAAAAAA: exceeds allowed range 00000000 ... FFFFFFFF at line 3 col 3");
170
+ });
171
+ test('error: bad string', () => {
172
+ const vkp = vkpParse(`
173
+ AAAAAAAA: FFFFFFFFFFFFFFFF "\\xAA"
174
+ AAAAAAAA: FFFFFFFFFFFFFFFF "\\u1234"
175
+ AAAAAAAA: FFFFFFFFFFFFFFFF "\\777"
176
+ AAAAAAAA: FFFFFFFFFFFFFFFF "\\jam"
177
+ `);
178
+ expect(vkp.valid).toBe(false);
179
+ expect(vkp.warnings.length).toBe(0);
180
+ expect(vkp.errors.length).toBe(4);
181
+ expect(vkp.errors[0]).toHaveProperty("message", "Bad escape sequence (\\xAA) at line 2 col 31\nAllowed range: \\x00-\\x7F.");
182
+ expect(vkp.errors[1]).toHaveProperty("message", "Unknown escape sequence (\\u1234) at line 3 col 31");
183
+ expect(vkp.errors[2]).toHaveProperty("message", "Unknown escape sequence (\\777) at line 4 col 31");
184
+ expect(vkp.errors[3]).toHaveProperty("message", "Unknown escape sequence (\\j) at line 5 col 31");
185
+ });
186
+ test('data: valid address & offset', () => {
187
+ const vkp = vkpParse(`
188
+ -123450
189
+ A8123456: AA BB
190
+ +0
191
+ `);
192
+ expect(vkp.valid).toBe(true);
193
+ expect(vkp.warnings.length).toBe(0);
194
+ expect(vkp.errors.length).toBe(0);
195
+ expect(vkp.writes.length).toBe(1);
196
+ expect(vkp.writes[0].addr).toBe(0xA8000006);
197
+ });
198
+ test('data: HEX bytes', () => {
199
+ const vkp = vkpParse(`00000000: FFFFFFFFFFFFFFFF DEAD926E,DE,AD,92,6E`);
200
+ expect(vkp.valid).toBe(true);
201
+ expect(vkp.warnings.length).toBe(0);
202
+ expect(vkp.errors.length).toBe(0);
203
+ expect(vkp.writes.length).toBe(1);
204
+ expect(vkp.writes[0].new.toString('hex')).toBe('dead926edead926e');
205
+ });
206
+ test('data: HEX numbers', () => {
207
+ const vkp = vkpParse(`00000000: FFFFFFFFFFFFFFFFFFFFFFFF 0xDEAD926E,0xDEAD,0x92,0x6E,0x1,0x2,0x123`);
208
+ expect(vkp.valid).toBe(true);
209
+ expect(vkp.warnings.length).toBe(0);
210
+ expect(vkp.errors.length).toBe(0);
211
+ expect(vkp.writes.length).toBe(1);
212
+ expect(vkp.writes[0].new.toString('hex')).toBe('6e92addeadde926e01022301');
213
+ });
214
+ test('data: binary numbers', () => {
215
+ const vkp = vkpParse(`00000000: FFFFFFFFFFFFFFFFFFFFFF 0n11011110101011011011111011101111,0n11011110,0n1101111010101101,0n100100011010001010110`);
216
+ expect(vkp.valid).toBe(true);
217
+ expect(vkp.warnings.length).toBe(0);
218
+ expect(vkp.errors.length).toBe(0);
219
+ expect(vkp.writes.length).toBe(1);
220
+ expect(vkp.writes[0].new.toString('hex')).toBe('efbeaddedeadde563412');
221
+ });
222
+ test('data: unsigned decimal numbers', () => {
223
+ const vkp = vkpParse(`
224
+ 00000000: FF 0i18
225
+ 00000000: FFFF 0i04660
226
+ 00000000: FFFFFF 0i01193046
227
+ 00000000: FFFFFFFF 0i0305419896
228
+ 00000000: FFFFFFFFFF 0i0078187493530
229
+ 00000000: FFFFFFFFFFFF 0i020015998343868
230
+ 00000000: FFFFFFFFFFFFFF 0i05124095576030430
231
+ 00000000: FFFFFFFFFFFFFFFF 0i01311768467463790320
232
+ `);
233
+ expect(vkp.valid).toBe(true);
234
+ expect(vkp.warnings.length).toBe(0);
235
+ expect(vkp.errors.length).toBe(0);
236
+ expect(vkp.writes.length).toBe(8);
237
+ expect(vkp.writes[0].new.toString('hex')).toBe('12');
238
+ expect(vkp.writes[1].new.toString('hex')).toBe('3412');
239
+ expect(vkp.writes[2].new.toString('hex')).toBe('563412');
240
+ expect(vkp.writes[3].new.toString('hex')).toBe('78563412');
241
+ expect(vkp.writes[4].new.toString('hex')).toBe('9a78563412');
242
+ expect(vkp.writes[5].new.toString('hex')).toBe('bc9a78563412');
243
+ expect(vkp.writes[6].new.toString('hex')).toBe('debc9a78563412');
244
+ expect(vkp.writes[7].new.toString('hex')).toBe('f0debc9a78563412');
245
+ });
246
+ test('data: positive decimal numbers', () => {
247
+ const vkp = vkpParse(`
248
+ ; middle value
249
+ 00000000: FF 0i+18
250
+ 00000000: FFFF 0i+04660
251
+ 00000000: FFFFFF 0i+01193046
252
+ 00000000: FFFFFFFF 0i+0305419896
253
+ 00000000: FFFFFFFFFF 0i+0078187493530
254
+ 00000000: FFFFFFFFFFFF 0i+020015998343868
255
+ 00000000: FFFFFFFFFFFFFF 0i+05124095576030430
256
+ 00000000: FFFFFFFFFFFFFFFF 0i+01311768467463790320
257
+
258
+ ; max value
259
+ 00000000: FF 0i+127
260
+ 00000000: FFFF 0i+32767
261
+ 00000000: FFFFFF 0i+08388607
262
+ 00000000: FFFFFFFF 0i+2147483647
263
+ 00000000: FFFFFFFFFF 0i+0549755813887
264
+ 00000000: FFFFFFFFFFFF 0i+140737488355327
265
+ 00000000: FFFFFFFFFFFFFF 0i+36028797018963967
266
+ 00000000: FFFFFFFFFFFFFFFF 0i+09223372036854775807
267
+
268
+ ; min value
269
+ 00000000: FF 0i+000
270
+ 00000000: FFFF 0i+00000
271
+ 00000000: FFFFFF 0i+00000000
272
+ 00000000: FFFFFFFF 0i+0000000000
273
+ 00000000: FFFFFFFFFF 0i+0000000000000
274
+ 00000000: FFFFFFFFFFFF 0i+000000000000000
275
+ 00000000: FFFFFFFFFFFFFF 0i+00000000000000000
276
+ 00000000: FFFFFFFFFFFFFFFF 0i+00000000000000000000
277
+ `);
278
+ expect(vkp.valid).toBe(true);
279
+ expect(vkp.warnings.length).toBe(0);
280
+ expect(vkp.errors.length).toBe(0);
281
+ expect(vkp.writes.length).toBe(24);
282
+ expect(vkp.writes[0].new.toString('hex')).toBe('12');
283
+ expect(vkp.writes[1].new.toString('hex')).toBe('3412');
284
+ expect(vkp.writes[2].new.toString('hex')).toBe('563412');
285
+ expect(vkp.writes[3].new.toString('hex')).toBe('78563412');
286
+ expect(vkp.writes[4].new.toString('hex')).toBe('9a78563412');
287
+ expect(vkp.writes[5].new.toString('hex')).toBe('bc9a78563412');
288
+ expect(vkp.writes[6].new.toString('hex')).toBe('debc9a78563412');
289
+ expect(vkp.writes[7].new.toString('hex')).toBe('f0debc9a78563412');
290
+ expect(vkp.writes[8].new.toString('hex')).toBe('7f');
291
+ expect(vkp.writes[9].new.toString('hex')).toBe('ff7f');
292
+ expect(vkp.writes[10].new.toString('hex')).toBe('ffff7f');
293
+ expect(vkp.writes[11].new.toString('hex')).toBe('ffffff7f');
294
+ expect(vkp.writes[12].new.toString('hex')).toBe('ffffffff7f');
295
+ expect(vkp.writes[13].new.toString('hex')).toBe('ffffffffff7f');
296
+ expect(vkp.writes[14].new.toString('hex')).toBe('ffffffffffff7f');
297
+ expect(vkp.writes[15].new.toString('hex')).toBe('ffffffffffffff7f');
298
+ expect(vkp.writes[16].new.toString('hex')).toBe('00');
299
+ expect(vkp.writes[17].new.toString('hex')).toBe('0000');
300
+ expect(vkp.writes[18].new.toString('hex')).toBe('000000');
301
+ expect(vkp.writes[19].new.toString('hex')).toBe('00000000');
302
+ expect(vkp.writes[20].new.toString('hex')).toBe('0000000000');
303
+ expect(vkp.writes[21].new.toString('hex')).toBe('000000000000');
304
+ expect(vkp.writes[22].new.toString('hex')).toBe('00000000000000');
305
+ expect(vkp.writes[23].new.toString('hex')).toBe('0000000000000000');
306
+ });
307
+ test('data: negative decimal numbers', () => {
308
+ const vkp = vkpParse(`
309
+ ; middle value
310
+ 00000000: FF 0i-18
311
+ 00000000: FFFF 0i-04660
312
+ 00000000: FFFFFF 0i-01193046
313
+ 00000000: FFFFFFFF 0i-0305419896
314
+ 00000000: FFFFFFFFFF 0i-0078187493530
315
+ 00000000: FFFFFFFFFFFF 0i-020015998343868
316
+ 00000000: FFFFFFFFFFFFFF 0i-05124095576030430
317
+ 00000000: FFFFFFFFFFFFFFFF 0i-01311768467463790320
318
+
319
+ ; min value
320
+ 00000000: FF 0i-127
321
+ 00000000: FFFF 0i-32767
322
+ 00000000: FFFFFF 0i-08388607
323
+ 00000000: FFFFFFFF 0i-2147483647
324
+ 00000000: FFFFFFFFFF 0i-0549755813887
325
+ 00000000: FFFFFFFFFFFF 0i-140737488355327
326
+ 00000000: FFFFFFFFFFFFFF 0i-36028797018963967
327
+ 00000000: FFFFFFFFFFFFFFFF 0i-09223372036854775807
328
+
329
+ ; max value
330
+ 00000000: FF 0i-001
331
+ 00000000: FFFF 0i-00001
332
+ 00000000: FFFFFF 0i-00000001
333
+ 00000000: FFFFFFFF 0i-0000000001
334
+ 00000000: FFFFFFFFFF 0i-0000000000001
335
+ 00000000: FFFFFFFFFFFF 0i-000000000000001
336
+ 00000000: FFFFFFFFFFFFFF 0i-00000000000000001
337
+ 00000000: FFFFFFFFFFFFFFFF 0i-00000000000000000001
338
+ `);
339
+ expect(vkp.valid).toBe(true);
340
+ expect(vkp.warnings.length).toBe(0);
341
+ expect(vkp.errors.length).toBe(0);
342
+ expect(vkp.writes.length).toBe(24);
343
+ expect(vkp.writes[0].new.toString('hex')).toBe('ee');
344
+ expect(vkp.writes[1].new.toString('hex')).toBe('cced');
345
+ expect(vkp.writes[2].new.toString('hex')).toBe('aacbed');
346
+ expect(vkp.writes[3].new.toString('hex')).toBe('88a9cbed');
347
+ expect(vkp.writes[4].new.toString('hex')).toBe('6687a9cbed');
348
+ expect(vkp.writes[5].new.toString('hex')).toBe('446587a9cbed');
349
+ expect(vkp.writes[6].new.toString('hex')).toBe('22436587a9cbed');
350
+ expect(vkp.writes[7].new.toString('hex')).toBe('1021436587a9cbed');
351
+ expect(vkp.writes[8].new.toString('hex')).toBe('81');
352
+ expect(vkp.writes[9].new.toString('hex')).toBe('0180');
353
+ expect(vkp.writes[10].new.toString('hex')).toBe('010080');
354
+ expect(vkp.writes[11].new.toString('hex')).toBe('01000080');
355
+ expect(vkp.writes[12].new.toString('hex')).toBe('0100000080');
356
+ expect(vkp.writes[13].new.toString('hex')).toBe('010000000080');
357
+ expect(vkp.writes[14].new.toString('hex')).toBe('01000000000080');
358
+ expect(vkp.writes[15].new.toString('hex')).toBe('0100000000000080');
359
+ expect(vkp.writes[16].new.toString('hex')).toBe('ff');
360
+ expect(vkp.writes[17].new.toString('hex')).toBe('ffff');
361
+ expect(vkp.writes[18].new.toString('hex')).toBe('ffffff');
362
+ expect(vkp.writes[19].new.toString('hex')).toBe('ffffffff');
363
+ expect(vkp.writes[20].new.toString('hex')).toBe('ffffffffff');
364
+ expect(vkp.writes[21].new.toString('hex')).toBe('ffffffffffff');
365
+ expect(vkp.writes[22].new.toString('hex')).toBe('ffffffffffffff');
366
+ expect(vkp.writes[23].new.toString('hex')).toBe('ffffffffffffffff');
367
+ });
368
+ test('data: string', () => {
369
+ const vkp = vkpParse(`
370
+ 00000000: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF "ололо\\
371
+ \\0\\177\\100test\\x50\\x20\\a\\b\\t\\r\\n\\v\\f\\e\\\\\\/"
372
+ 00000000: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 'ололо\\
373
+ \\0\\177\\100\\uABCDtest\\xAB\\xCD\\a\\b\\t\\r\\n\\v\\f\\e\\\\\\/'
374
+ `);
375
+ expect(vkp.valid).toBe(true);
376
+ expect(vkp.warnings.length).toBe(0);
377
+ expect(vkp.errors.length).toBe(0);
378
+ expect(vkp.writes.length).toBe(2);
379
+ expect(vkp.writes[0].new.toString('hex')).toBe('eeebeeebee007f407465737450200708090d0a0b0c1b5c2f');
380
+ expect(vkp.writes[1].new.toString('hex')).toBe('3e043b043e043b043e0400007f004000cdab7400650073007400ab00cd000700080009000d000a000b000c001b005c002f00');
381
+ });