@springfield/ham-radio-utils 4.15.4 → 4.16.1
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/dist/memory/memory-map-codec.d.ts.map +1 -1
- package/dist/memory/memory-map-codec.js +45 -19
- package/dist/memory/memory-map-codec.js.map +1 -1
- package/dist/schemas/radio-protocol-schema.json +25 -0
- package/package.json +7 -7
- package/src/memory/memory-map-codec.ts +70 -24
- package/src/schemas/radio-protocol-schema.json +25 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-map-codec.d.ts","sourceRoot":"","sources":["../../src/memory/memory-map-codec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EAEd,oBAAoB,EAEpB,aAAa,EAEd,MAAM,4BAA4B,CAAC;AAIpC;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAY9D;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,iBAAiB,EAC/B,YAAY,EAAE,MAAM,GACnB,MAAM,CAmBR;AAgDD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAcjG;
|
|
1
|
+
{"version":3,"file":"memory-map-codec.d.ts","sourceRoot":"","sources":["../../src/memory/memory-map-codec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EAEd,oBAAoB,EAEpB,aAAa,EAEd,MAAM,4BAA4B,CAAC;AAIpC;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAY9D;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,iBAAiB,EAC/B,YAAY,EAAE,MAAM,GACnB,MAAM,CAmBR;AAgDD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAcjG;AA4jBD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,cAAc,EACzB,QAAQ,EAAE,UAAU,EACpB,YAAY,EAAE,iBAAiB,GAC9B,aAAa,CAwBf;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,cAAc,EACzB,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,UAAU,EACpB,YAAY,EAAE,iBAAiB,GAC9B,UAAU,CA+BZ"}
|
|
@@ -83,6 +83,39 @@ export function structInstanceAddress(struct, instanceIndex) {
|
|
|
83
83
|
const indexInGroup = instanceIndex % groupSize;
|
|
84
84
|
return seek + groupIndex * (stride * groupSize + groupPad) + indexInGroup * stride;
|
|
85
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* ASCII fields stop at NUL or 0xFF, then drop trailing spaces. The bytes after
|
|
88
|
+
* the visible text are not part of the setting value.
|
|
89
|
+
*/
|
|
90
|
+
function decodeAscii(bytes) {
|
|
91
|
+
let text = '';
|
|
92
|
+
for (const byte of bytes) {
|
|
93
|
+
if (byte === 0xff || byte === 0x00) {
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
text += String.fromCodePoint(byte);
|
|
97
|
+
}
|
|
98
|
+
return text.trimEnd();
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Clone images mix space, NUL, and 0xFF padding in one ASCII field. Re-encoding
|
|
102
|
+
* an unchanged value with the single declared pad rewrites those bytes (a
|
|
103
|
+
* TH-D74 power-on message ending in spaces came back NUL-padded; a D-STAR memo
|
|
104
|
+
* ending in NUL came back space-padded). When the text still matches what
|
|
105
|
+
* decode would return, keep the stored bytes. Edited text uses `kind.pad`.
|
|
106
|
+
*/
|
|
107
|
+
function encodeAscii(kind, value, byteLength, originalBytes) {
|
|
108
|
+
const text = typeof value === 'string' ? value : '';
|
|
109
|
+
if (originalBytes && originalBytes.length === byteLength && decodeAscii(originalBytes) === text) {
|
|
110
|
+
return [...originalBytes];
|
|
111
|
+
}
|
|
112
|
+
const pad = kind.pad ?? 0xff;
|
|
113
|
+
const bytes = [];
|
|
114
|
+
for (let index = 0; index < byteLength; index += 1) {
|
|
115
|
+
bytes[index] = index < text.length ? (text.codePointAt(index) ?? pad) : pad;
|
|
116
|
+
}
|
|
117
|
+
return bytes;
|
|
118
|
+
}
|
|
86
119
|
function decodeRawValue(kind, raw) {
|
|
87
120
|
if (!kind) {
|
|
88
121
|
return typeof raw === 'number' ? raw : raw;
|
|
@@ -98,14 +131,7 @@ function decodeRawValue(kind, raw) {
|
|
|
98
131
|
}
|
|
99
132
|
case 'ascii': {
|
|
100
133
|
const bytes = typeof raw === 'number' ? [raw] : raw;
|
|
101
|
-
|
|
102
|
-
for (const byte of bytes) {
|
|
103
|
-
if (byte === 0xff || byte === 0x00) {
|
|
104
|
-
break;
|
|
105
|
-
}
|
|
106
|
-
text += String.fromCodePoint(byte);
|
|
107
|
-
}
|
|
108
|
-
return text.trimEnd();
|
|
134
|
+
return decodeAscii(bytes);
|
|
109
135
|
}
|
|
110
136
|
case 'digits': {
|
|
111
137
|
const bytes = typeof raw === 'number' ? [raw] : raw;
|
|
@@ -186,7 +212,7 @@ function decodeRawValue(kind, raw) {
|
|
|
186
212
|
}
|
|
187
213
|
}
|
|
188
214
|
}
|
|
189
|
-
function encodeRawValue(kind, value, byteLength) {
|
|
215
|
+
function encodeRawValue(kind, value, byteLength, originalBytes) {
|
|
190
216
|
const bytes = Array.from({ length: byteLength }, () => 0xff);
|
|
191
217
|
if (!kind) {
|
|
192
218
|
if (typeof value === 'number') {
|
|
@@ -207,14 +233,8 @@ function encodeRawValue(kind, value, byteLength) {
|
|
|
207
233
|
bytes[0] = index >= 0 ? index : 0;
|
|
208
234
|
return bytes;
|
|
209
235
|
}
|
|
210
|
-
case 'ascii':
|
|
211
|
-
|
|
212
|
-
const pad = kind.pad ?? 0xff;
|
|
213
|
-
for (let index = 0; index < byteLength; index += 1) {
|
|
214
|
-
bytes[index] = index < text.length ? (text.codePointAt(index) ?? pad) : pad;
|
|
215
|
-
}
|
|
216
|
-
return bytes;
|
|
217
|
-
}
|
|
236
|
+
case 'ascii':
|
|
237
|
+
return encodeAscii(kind, value, byteLength, originalBytes);
|
|
218
238
|
case 'digits': {
|
|
219
239
|
const scale = kind.scale ?? 1;
|
|
220
240
|
let numeric = typeof value === 'number' ? Math.round(value / scale) : 0;
|
|
@@ -466,14 +486,20 @@ function encodeStruct(struct, values, contents, memoryConfig, instanceIndex) {
|
|
|
466
486
|
continue;
|
|
467
487
|
}
|
|
468
488
|
const settingValue = values[field.id];
|
|
489
|
+
const originalBytes = field.value?.kind === 'ascii' ? [] : undefined;
|
|
490
|
+
if (originalBytes) {
|
|
491
|
+
for (let index = 0; index < length; index += 1) {
|
|
492
|
+
originalBytes.push(readByte(contents, bufferOffsetFor(radioAddress + index, memoryConfig, contents)));
|
|
493
|
+
}
|
|
494
|
+
}
|
|
469
495
|
if (field.type === 'u16' || field.type === 'u32') {
|
|
470
|
-
const encoded = encodeRawValue(field.value ?? { kind: 'integer' }, settingValue ?? 0, length);
|
|
496
|
+
const encoded = encodeRawValue(field.value ?? { kind: 'integer' }, settingValue ?? 0, length, originalBytes);
|
|
471
497
|
for (let index = 0; index < length; index += 1) {
|
|
472
498
|
writeByte(contents, bufferOffsetFor(radioAddress + index, memoryConfig, contents), encoded[index] ?? 0);
|
|
473
499
|
}
|
|
474
500
|
}
|
|
475
501
|
else {
|
|
476
|
-
const encoded = encodeRawValue(field.value, settingValue ?? (field.value?.kind === 'ascii' ? '' : 0), length);
|
|
502
|
+
const encoded = encodeRawValue(field.value, settingValue ?? (field.value?.kind === 'ascii' ? '' : 0), length, originalBytes);
|
|
477
503
|
for (let index = 0; index < length; index += 1) {
|
|
478
504
|
writeByte(contents, bufferOffsetFor(radioAddress + index, memoryConfig, contents), encoded[index]);
|
|
479
505
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-map-codec.js","sourceRoot":"","sources":["../../src/memory/memory-map-codec.ts"],"names":[],"mappings":"AAUA,MAAM,oBAAoB,GAAG,mBAAmB,CAAC;AAEjD;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAqB;IACpD,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAE5B,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,OAAO,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,OAAO,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CACxC,YAAoB,EACpB,YAA+B,EAC/B,YAAoB;IAEpB,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;IACtD,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjF,IAAI,YAAY,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;QACtC,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,YAAY,IAAI,OAAO,CAAC,YAAY,IAAI,YAAY,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;YAC/E,OAAO,MAAM,GAAG,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,IAAI,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,YAAY,GAAG,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,mBAAmB,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,+BAA+B,CAAC,CAAC;AAC/F,CAAC;AAED,SAAS,eAAe,CAAC,YAAoB,EAAE,YAA+B,EAAE,QAAoB;IAClG,OAAO,0BAA0B,CAAC,YAAY,EAAE,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,QAAQ,CAAC,QAAoB,EAAE,MAAc;IACpD,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC5C,MAAM,IAAI,UAAU,CAAC,2CAA2C,MAAM,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,SAAS,CAAC,QAAoB,EAAE,MAAc,EAAE,KAAa;IACpE,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC5C,MAAM,IAAI,UAAU,CAAC,4CAA4C,MAAM,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,QAAQ,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC;AAClC,CAAC;AAED,SAAS,aAAa,CAAC,GAAsB;IAC3C,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACnD,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,KAAK,CAAC;IACrC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,KAAwB,EAAE,UAAkB;IACjE,IAAI,OAAO,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACnD,KAAK,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;QAC3B,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,CAAC;IACtC,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAA4B,EAAE,aAAqB;IACvF,MAAM,IAAI,GAAG,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IAClC,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IAEnC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,IAAI,GAAG,aAAa,GAAG,MAAM,CAAC;IACvC,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,SAAS,CAAC,CAAC;IACzD,MAAM,YAAY,GAAG,aAAa,GAAG,SAAS,CAAC;IAE/C,OAAO,IAAI,GAAG,UAAU,GAAG,CAAC,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC,GAAG,YAAY,GAAG,MAAM,CAAC;AACrF,CAAC;AAED,SAAS,cAAc,CAAC,IAAyC,EAAE,GAAsB;IACvF,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,OAAO,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IAC7C,CAAC;IAED,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,SAAS;YACZ,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC;QAC5B,KAAK,SAAS;YACZ,OAAO,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACxD,KAAK,MAAM,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACrD,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACpD,CAAC;QACD,KAAK,OAAO,EAAE,CAAC;YACb,MAAM,KAAK,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YACpD,IAAI,IAAI,GAAG,EAAE,CAAC;YAEd,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;oBACnC,MAAM;gBACR,CAAC;gBAED,IAAI,IAAI,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YACrC,CAAC;YAED,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;QACxB,CAAC;QACD,KAAK,QAAQ,EAAE,CAAC;YACd,MAAM,KAAK,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YACpD,IAAI,KAAK,GAAG,CAAC,CAAC;YAEd,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;gBAC1B,KAAK,GAAG,KAAK,GAAG,EAAE,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;YACtC,CAAC;YAED,OAAO,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;QACnC,CAAC;QACD,KAAK,MAAM,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YACpD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,oBAAoB,CAAC;YACrD,IAAI,IAAI,GAAG,EAAE,CAAC;YAEd,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;oBACjB,MAAM;gBACR,CAAC;gBAED,IAAI,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;oBAC1B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,MAAM,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YACpD,IAAI,KAAK,GAAG,CAAC,CAAC;YAEd,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,KAAK,GAAG,KAAK,GAAG,GAAG,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;YAClE,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,MAAM,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YACpD,IAAI,IAAI,GAAG,CAAC,CAAC;YAEb,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBACrD,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;YACtC,CAAC;YAED,IAAI,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;gBACnE,OAAO,IAAI,CAAC;YACd,CAAC;YAED,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QACrE,CAAC;QACD,KAAK,MAAM,EAAE,CAAC;YACZ,MAAM,QAAQ,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;YACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC;YACzC,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC;YAEjD,IAAI,QAAQ,KAAK,CAAC,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;gBAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YAC1B,CAAC;YAED,IAAI,QAAQ,IAAI,QAAQ,EAAE,CAAC;gBACzB,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;YAC5C,CAAC;YAED,IAAI,KAAK,GAAG,QAAQ,CAAC;YACrB,IAAI,QAAQ,GAAc,GAAG,CAAC;YAE9B,IAAI,QAAQ,GAAG,aAAa,EAAE,CAAC;gBAC7B,KAAK,GAAG,QAAQ,GAAG,aAAa,CAAC;gBACjC,QAAQ,GAAG,GAAG,CAAC;YACjB,CAAC;iBAAM,CAAC;gBACN,KAAK,GAAG,QAAQ,GAAG,CAAC,CAAC;YACvB,CAAC;YAED,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACrC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QACzC,CAAC;QACD,KAAK,aAAa,EAAE,CAAC;YACnB,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;YACjC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACzD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAC1C,CAAC;QACD,KAAK,WAAW,EAAE,CAAC;YACjB,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;YACjC,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACrC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;QAC9C,CAAC;QACD,SAAS,CAAC;YACR,MAAM,UAAU,GAAU,IAAI,CAAC;YAC/B,OAAO,UAAU,CAAC;QACpB,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,IAAyC,EAAE,KAAwB,EAAE,UAAkB;IAC7G,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAE7D,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC;QAC1B,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,SAAS,EAAE,CAAC;YACf,OAAO,aAAa,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;QAC1C,CAAC;QACD,KAAK,SAAS,EAAE,CAAC;YACf,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACzB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,MAAM,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1E,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAClC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC;YAE7B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBACnD,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YAC9E,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,QAAQ,EAAE,CAAC;YACd,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;YAC9B,IAAI,OAAO,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAExE,KAAK,IAAI,KAAK,GAAG,UAAU,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBACxD,KAAK,CAAC,KAAK,CAAC,GAAG,OAAO,GAAG,EAAE,CAAC;gBAC5B,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;YACrC,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,oBAAoB,CAAC;YAErD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBACnD,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;oBACxB,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;oBAC/C,KAAK,CAAC,KAAK,CAAC,GAAG,SAAS,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;gBACnD,CAAC;qBAAM,CAAC;oBACN,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;gBACtB,CAAC;YACH,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,MAAM,EAAE,CAAC;YACZ,IAAI,OAAO,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACpD,MAAM,MAAM,GAAa,EAAE,CAAC;YAE5B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBACvD,MAAM,CAAC,OAAO,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;gBAC7B,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;YACrC,CAAC;YAED,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBACnD,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;YACpF,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,MAAM,EAAE,CAAC;YACZ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBAC1C,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACxD,CAAC;YAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/B,MAAM,EAAE,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACjD,IAAI,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YAEpE,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,UAAU,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBACnD,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;gBAC3B,IAAI,KAAK,CAAC,CAAC;YACb,CAAC;YAED,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,MAAM,EAAE,CAAC;YACZ,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC;YACzC,MAAM,aAAa,GAAG,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC;YACjD,IAAI,QAAQ,GAAG,CAAC,CAAC;YAEjB,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBAChE,MAAM,IAAI,GAAG,KAA4E,CAAC;gBAE1F,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;oBAC5D,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC;gBACxB,CAAC;qBAAM,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBAChE,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC7C,QAAQ,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;oBAEtC,IAAI,IAAI,CAAC,QAAQ,KAAK,GAAG,EAAE,CAAC;wBAC1B,QAAQ,IAAI,aAAa,CAAC;oBAC5B,CAAC;gBACH,CAAC;YACH,CAAC;YAED,IAAI,QAAQ,IAAI,QAAQ,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;gBAC3C,8BAA8B;YAChC,CAAC;YAED,KAAK,CAAC,CAAC,CAAC,GAAG,QAAQ,GAAG,IAAI,CAAC;YAC3B,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC;YAClC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,KAAK,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,GAAG,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,KAA2C,CAAC,CAAC,CAAC,SAAS,CAAC;YACpI,MAAM,MAAM,GAAG,IAAI,EAAE,IAAI,KAAK,OAAO,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACzF,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC1C,OAAO,aAAa,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAC3D,CAAC;QACD,KAAK,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,KAA0C,CAAC,CAAC,CAAC,SAAS,CAAC;YACnI,MAAM,IAAI,GAAG,IAAI,EAAE,IAAI,KAAK,KAAK,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACnF,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACxC,OAAO,aAAa,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAC3D,CAAC;QACD,SAAS,CAAC;YACR,MAAM,UAAU,GAAU,IAAI,CAAC;YAC/B,OAAO,UAAU,CAAC;QACpB,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,KAA0B;IACjD,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QACzB,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC1B,OAAO,CAAC,CAAC;IACX,CAAC;IAED,IACE,KAAK,CAAC,KAAK,EAAE,IAAI,KAAK,OAAO;QAC7B,KAAK,CAAC,KAAK,EAAE,IAAI,KAAK,QAAQ;QAC9B,KAAK,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM;QAC5B,KAAK,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM;QAC5B,KAAK,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM,EAC5B,CAAC;QACD,OAAO,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;QACjC,OAAO,CAAC,CAAC;IACX,CAAC;IAED,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;GAEG;AACH,MAAM,cAAc;IAOC,QAAQ;IACR,YAAY;IAPvB,YAAY,CAAS;IACrB,QAAQ,GAAG,CAAC,CAAC,CAAC;IACd,WAAW,GAAG,CAAC,CAAC;IAChB,KAAK,GAAG,KAAK,CAAC;IAEtB,YACmB,QAAoB,EACpB,YAA+B,EAChD,iBAAyB;wBAFR,QAAQ;4BACR,YAAY;QAG7B,IAAI,CAAC,YAAY,GAAG,iBAAiB,CAAC;IACxC,CAAC;IAEO,IAAI;QACV,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACjH,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAEO,SAAS;QACf,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACf,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;YACjH,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACrB,CAAC;IACH,CAAC;IAED,IAAI,CAAC,KAAa;QAChB,IAAI,MAAM,GAAG,CAAC,CAAC;QAEf,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;YACxC,IAAI,CAAC,IAAI,EAAE,CAAC;YACZ,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;YACnE,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YAEnB,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;gBACtB,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,KAAa,EAAE,KAAa;QAChC,KAAK,IAAI,GAAG,GAAG,KAAK,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;YAC7C,IAAI,CAAC,IAAI,EAAE,CAAC;YACZ,MAAM,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC;YAEhC,IAAI,CAAC,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvB,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC;YAC3B,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC;YAC5B,CAAC;YAED,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;YAClB,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YAEnB,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;gBACtB,IAAI,CAAC,SAAS,EAAE,CAAC;gBACjB,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,gBAAgB;QACd,IAAI,CAAC,SAAS,EAAE,CAAC;QAEjB,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QAC/B,CAAC;QAED,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;CACF;AAED,SAAS,YAAY,CACnB,MAA4B,EAC5B,QAAoB,EACpB,YAA+B,EAC/B,aAAqB;IAErB,MAAM,IAAI,GAAG,qBAAqB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAsC,EAAE,CAAC;IACrD,IAAI,YAAY,GAAG,IAAI,CAAC;IACxB,IAAI,SAAqC,CAAC;IAE1C,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,SAAS,GAAG,IAAI,cAAc,CAAC,QAAQ,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;YACvE,CAAC;YAED,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;YAE7C,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACpB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,GAAG,CAAC,CAAC;YAC7E,CAAC;YAED,SAAS;QACX,CAAC;QAED,IAAI,SAAS,EAAE,CAAC;YACd,YAAY,GAAG,SAAS,CAAC,gBAAgB,EAAE,CAAC;YAC5C,SAAS,GAAG,SAAS,CAAC;QACxB,CAAC;QAED,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;YAC/C,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC,YAAY,GAAG,KAAK,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;QAChG,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACjD,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACpB,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,CAAC,CAAC;YAC/E,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC3B,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAClF,CAAC;QAED,YAAY,IAAI,MAAM,CAAC;IACzB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,YAAY,CACnB,MAA4B,EAC5B,MAAyC,EACzC,QAAoB,EACpB,YAA+B,EAC/B,aAAqB;IAErB,MAAM,IAAI,GAAG,qBAAqB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC1D,IAAI,YAAY,GAAG,IAAI,CAAC;IACxB,IAAI,SAAqC,CAAC;IAC1C,IAAI,eAAe,GAAG,IAAI,CAAC;IAE3B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,eAAe,GAAG,YAAY,CAAC;gBAC/B,SAAS,GAAG,IAAI,cAAc,CAAC,QAAQ,EAAE,YAAY,EAAE,eAAe,CAAC,CAAC;YAC1E,CAAC;YAED,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;YAE/B,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACnB,sDAAsD;gBACtD,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACxB,CAAC;iBAAM,CAAC;gBACN,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC7F,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YACrC,CAAC;YAED,SAAS;QACX,CAAC;QAED,IAAI,SAAS,EAAE,CAAC;YACd,YAAY,GAAG,SAAS,CAAC,gBAAgB,EAAE,CAAC;YAC5C,SAAS,GAAG,SAAS,CAAC;QACxB,CAAC;QAED,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;QAEtC,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACnB,YAAY,IAAI,MAAM,CAAC;YACvB,SAAS;QACX,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAEtC,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YACjD,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,YAAY,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;YAE9F,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBAC/C,SAAS,CAAC,QAAQ,EAAE,eAAe,CAAC,YAAY,GAAG,KAAK,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YAC1G,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,OAAO,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,YAAY,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;YAE9G,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBAC/C,SAAS,CAAC,QAAQ,EAAE,eAAe,CAAC,YAAY,GAAG,KAAK,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;YACrG,CAAC;QACH,CAAC;QAED,YAAY,IAAI,MAAM,CAAC;IACzB,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACd,SAAS,CAAC,gBAAgB,EAAE,CAAC;IAC/B,CAAC;AACH,CAAC;AAED,SAAS,qBAAqB,CAC5B,MAA4B,EAC5B,QAAoB,EACpB,YAA+B,EAC/B,aAAqB;IAErB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,IAAI,GAAG,qBAAqB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC1D,MAAM,SAAS,GAAG,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC,CAAC;IACpF,OAAO,SAAS,KAAK,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC;AAC/C,CAAC;AAED,SAAS,mBAAmB,CAC1B,MAA4B,EAC5B,QAAoB,EACpB,YAA+B,EAC/B,aAAqB;IAErB,MAAM,IAAI,GAAG,qBAAqB,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC;IAEnC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC/C,SAAS,CAAC,QAAQ,EAAE,eAAe,CAAC,IAAI,GAAG,KAAK,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;IACnF,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,SAAyB,EACzB,QAAoB,EACpB,YAA+B;IAE/B,MAAM,QAAQ,GAAkB,EAAE,CAAC;IAEnC,KAAK,MAAM,MAAM,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;QAEhC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,MAAM,KAAK,GAAwB,EAAE,CAAC;YAEtC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBAC9C,IAAI,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC;oBACjE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACnB,CAAC;qBAAM,CAAC;oBACN,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC,CAAC;gBAClE,CAAC;YACH,CAAC;YAED,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC;QAC9B,CAAC;aAAM,CAAC;YACN,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,SAAyB,EACzB,QAAuB,EACvB,QAAoB,EACpB,YAA+B;IAE/B,KAAK,MAAM,MAAM,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAElC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YAEhD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;gBAE1B,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBACxC,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;wBACtB,mBAAmB,CAAC,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;oBAC7D,CAAC;oBAED,SAAS;gBACX,CAAC;gBAED,MAAM,MAAM,GACV,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAE,IAA0C,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC9G,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,GACV,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,KAA2C,CAAC,CAAC,CAAC,EAAE,CAAC;YAClH,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC","sourcesContent":["import type {\n RadioMemoryConfig,\n RadioMemoryMap,\n RadioMemoryMapField,\n RadioMemoryMapStruct,\n RadioMemoryMapValueKind,\n RadioSettings,\n RadioSettingValue,\n} from '@springfield/ham-radio-api';\n\nconst DEFAULT_DTMF_CHARSET = '0123456789 *#ABCD';\n\n/**\n * Parse a seek address from a number or hex/decimal string.\n */\nexport function parseSeekAddress(seek: number | string): number {\n if (typeof seek === 'number') {\n return seek;\n }\n\n const trimmed = seek.trim();\n\n if (/^0x/i.test(trimmed)) {\n return Number.parseInt(trimmed, 16);\n }\n\n return Number.parseInt(trimmed, 10);\n}\n\n/**\n * Map a radio EEPROM address to an offset in the memory buffer.\n *\n * Packed buffers (driver read) concatenate segments in config order.\n * Sparse buffers that cover the highest segment end address use absolute offsets.\n */\nexport function radioAddressToBufferOffset(\n radioAddress: number,\n memoryConfig: RadioMemoryConfig,\n bufferLength: number,\n): number {\n const segments = Object.values(memoryConfig.segments);\n const maxEndAddress = Math.max(...segments.map((segment) => segment.endAddress));\n\n if (bufferLength >= maxEndAddress + 1) {\n return radioAddress;\n }\n\n let offset = 0;\n\n for (const segment of segments) {\n if (radioAddress >= segment.startAddress && radioAddress <= segment.endAddress) {\n return offset + (radioAddress - segment.startAddress);\n }\n\n offset += segment.endAddress - segment.startAddress + 1;\n }\n\n throw new Error(`Radio address 0x${radioAddress.toString(16)} is not in any memory segment`);\n}\n\nfunction bufferOffsetFor(radioAddress: number, memoryConfig: RadioMemoryConfig, contents: Uint8Array): number {\n return radioAddressToBufferOffset(radioAddress, memoryConfig, contents.length);\n}\n\nfunction readByte(contents: Uint8Array, offset: number): number {\n if (offset < 0 || offset >= contents.length) {\n throw new RangeError(`Memory-map read out of bounds at offset ${offset}`);\n }\n\n return contents[offset];\n}\n\nfunction writeByte(contents: Uint8Array, offset: number, value: number): void {\n if (offset < 0 || offset >= contents.length) {\n throw new RangeError(`Memory-map write out of bounds at offset ${offset}`);\n }\n\n contents[offset] = value & 0xff;\n}\n\nfunction decodeInteger(raw: number | number[]): number {\n if (typeof raw === 'number') {\n return raw;\n }\n\n let value = 0;\n\n for (let index = 0; index < raw.length; index += 1) {\n value += raw[index] * 256 ** index;\n }\n\n return value;\n}\n\nfunction encodeInteger(value: RadioSettingValue, byteLength: number): number[] {\n let numeric = typeof value === 'number' ? value : 0;\n const bytes: number[] = [];\n\n for (let index = 0; index < byteLength; index += 1) {\n bytes.push(numeric & 0xff);\n numeric = Math.floor(numeric / 256);\n }\n\n return bytes;\n}\n\n/**\n * Radio address of repeated-struct instance `index`, including Kenwood-style\n * grouped stride (N records + pad bytes per clone block).\n */\nexport function structInstanceAddress(struct: RadioMemoryMapStruct, instanceIndex: number): number {\n const seek = parseSeekAddress(struct.seek);\n const stride = struct.stride ?? 0;\n const groupSize = struct.groupSize;\n\n if (!groupSize) {\n return seek + instanceIndex * stride;\n }\n\n const groupPad = struct.groupPad ?? 0;\n const groupIndex = Math.floor(instanceIndex / groupSize);\n const indexInGroup = instanceIndex % groupSize;\n\n return seek + groupIndex * (stride * groupSize + groupPad) + indexInGroup * stride;\n}\n\nfunction decodeRawValue(kind: RadioMemoryMapValueKind | undefined, raw: number | number[]): RadioSettingValue {\n if (!kind) {\n return typeof raw === 'number' ? raw : raw;\n }\n\n switch (kind.kind) {\n case 'integer':\n return decodeInteger(raw);\n case 'boolean':\n return (typeof raw === 'number' ? raw : raw[0]) !== 0;\n case 'enum': {\n const index = typeof raw === 'number' ? raw : raw[0];\n return kind.values[index] ?? kind.values[0] ?? '';\n }\n case 'ascii': {\n const bytes = typeof raw === 'number' ? [raw] : raw;\n let text = '';\n\n for (const byte of bytes) {\n if (byte === 0xff || byte === 0x00) {\n break;\n }\n\n text += String.fromCodePoint(byte);\n }\n\n return text.trimEnd();\n }\n case 'digits': {\n const bytes = typeof raw === 'number' ? [raw] : raw;\n let value = 0;\n\n for (const digit of bytes) {\n value = value * 10 + (digit & 0x0f);\n }\n\n return value * (kind.scale ?? 1);\n }\n case 'dtmf': {\n const bytes = typeof raw === 'number' ? [raw] : raw;\n const charset = kind.charset ?? DEFAULT_DTMF_CHARSET;\n let text = '';\n\n for (const byte of bytes) {\n if (byte >= 0x1f) {\n break;\n }\n\n if (byte < charset.length) {\n text += charset[byte];\n }\n }\n\n return text;\n }\n case 'bbcd': {\n const bytes = typeof raw === 'number' ? [raw] : raw;\n let value = 0;\n\n for (const byte of bytes) {\n value = value * 100 + ((byte >> 4) & 0x0f) * 10 + (byte & 0x0f);\n }\n\n return value;\n }\n case 'lbcd': {\n const bytes = typeof raw === 'number' ? [raw] : raw;\n let word = 0;\n\n for (let index = 0; index < bytes.length; index += 1) {\n word |= bytes[index] << (8 * index);\n }\n\n if (word === 0xff_ff_ff_ff || bytes.every((byte) => byte === 0xff)) {\n return null;\n }\n\n return Number.parseInt(word.toString(16), 10) * (kind.scale ?? 10);\n }\n case 'tone': {\n const rawValue = typeof raw === 'number' ? raw : raw[0] | (raw[1] << 8);\n const ctcssMin = kind.ctcssMin ?? 0x0258;\n const reverseOffset = kind.reverseOffset ?? 0x69;\n\n if (rawValue === 0 || rawValue === 0xffff) {\n return { mode: 'none' };\n }\n\n if (rawValue >= ctcssMin) {\n return { mode: 'ctcss', value: rawValue };\n }\n\n let index = rawValue;\n let polarity: 'N' | 'R' = 'N';\n\n if (rawValue > reverseOffset) {\n index = rawValue - reverseOffset;\n polarity = 'R';\n } else {\n index = rawValue - 1;\n }\n\n const code = kind.values[index] ?? 0;\n return { mode: 'dcs', code, polarity };\n }\n case 'ctcss-index': {\n const index = decodeInteger(raw);\n const tenths = kind.values[index] ?? kind.values[0] ?? 0;\n return { mode: 'ctcss', value: tenths };\n }\n case 'dcs-index': {\n const index = decodeInteger(raw);\n const code = kind.values[index] ?? 0;\n return { mode: 'dcs', code, polarity: 'N' };\n }\n default: {\n const exhaustive: never = kind;\n return exhaustive;\n }\n }\n}\n\nfunction encodeRawValue(kind: RadioMemoryMapValueKind | undefined, value: RadioSettingValue, byteLength: number): number[] {\n const bytes = Array.from({ length: byteLength }, () => 0xff);\n\n if (!kind) {\n if (typeof value === 'number') {\n bytes[0] = value & 0xff;\n }\n\n return bytes;\n }\n\n switch (kind.kind) {\n case 'integer': {\n return encodeInteger(value, byteLength);\n }\n case 'boolean': {\n bytes[0] = value ? 1 : 0;\n return bytes;\n }\n case 'enum': {\n const index = typeof value === 'string' ? kind.values.indexOf(value) : -1;\n bytes[0] = index >= 0 ? index : 0;\n return bytes;\n }\n case 'ascii': {\n const text = typeof value === 'string' ? value : '';\n const pad = kind.pad ?? 0xff;\n\n for (let index = 0; index < byteLength; index += 1) {\n bytes[index] = index < text.length ? (text.codePointAt(index) ?? pad) : pad;\n }\n\n return bytes;\n }\n case 'digits': {\n const scale = kind.scale ?? 1;\n let numeric = typeof value === 'number' ? Math.round(value / scale) : 0;\n\n for (let index = byteLength - 1; index >= 0; index -= 1) {\n bytes[index] = numeric % 10;\n numeric = Math.floor(numeric / 10);\n }\n\n return bytes;\n }\n case 'dtmf': {\n const text = typeof value === 'string' ? value : '';\n const charset = kind.charset ?? DEFAULT_DTMF_CHARSET;\n\n for (let index = 0; index < byteLength; index += 1) {\n if (index < text.length) {\n const charIndex = charset.indexOf(text[index]);\n bytes[index] = charIndex >= 0 ? charIndex : 0xff;\n } else {\n bytes[index] = 0xff;\n }\n }\n\n return bytes;\n }\n case 'bbcd': {\n let numeric = typeof value === 'number' ? value : 0;\n const digits: number[] = [];\n\n for (let index = 0; index < byteLength * 2; index += 1) {\n digits.unshift(numeric % 10);\n numeric = Math.floor(numeric / 10);\n }\n\n for (let index = 0; index < byteLength; index += 1) {\n bytes[index] = ((digits[index * 2] & 0x0f) << 4) | (digits[index * 2 + 1] & 0x0f);\n }\n\n return bytes;\n }\n case 'lbcd': {\n if (value === null || value === undefined) {\n return Array.from({ length: byteLength }, () => 0xff);\n }\n\n const scale = kind.scale ?? 10;\n const hz = typeof value === 'number' ? value : 0;\n let word = Number.parseInt(Math.round(hz / scale).toString(10), 16);\n\n for (let index = 0; index < byteLength; index += 1) {\n bytes[index] = word & 0xff;\n word >>= 8;\n }\n\n return bytes;\n }\n case 'tone': {\n const ctcssMin = kind.ctcssMin ?? 0x0258;\n const reverseOffset = kind.reverseOffset ?? 0x69;\n let rawValue = 0;\n\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const tone = value as { mode?: string; value?: number; code?: number; polarity?: string };\n\n if (tone.mode === 'ctcss' && typeof tone.value === 'number') {\n rawValue = tone.value;\n } else if (tone.mode === 'dcs' && typeof tone.code === 'number') {\n const index = kind.values.indexOf(tone.code);\n rawValue = index >= 0 ? index + 1 : 0;\n\n if (tone.polarity === 'R') {\n rawValue += reverseOffset;\n }\n }\n }\n\n if (rawValue >= ctcssMin || rawValue === 0) {\n // CTCSS or none: write LE u16\n }\n\n bytes[0] = rawValue & 0xff;\n bytes[1] = (rawValue >> 8) & 0xff;\n return bytes;\n }\n case 'ctcss-index': {\n const tone = value && typeof value === 'object' && !Array.isArray(value) ? (value as { mode?: string; value?: number }) : undefined;\n const tenths = tone?.mode === 'ctcss' && typeof tone.value === 'number' ? tone.value : 0;\n const index = kind.values.indexOf(tenths);\n return encodeInteger(index >= 0 ? index : 0, byteLength);\n }\n case 'dcs-index': {\n const tone = value && typeof value === 'object' && !Array.isArray(value) ? (value as { mode?: string; code?: number }) : undefined;\n const code = tone?.mode === 'dcs' && typeof tone.code === 'number' ? tone.code : 0;\n const index = kind.values.indexOf(code);\n return encodeInteger(index >= 0 ? index : 0, byteLength);\n }\n default: {\n const exhaustive: never = kind;\n return exhaustive;\n }\n }\n}\n\nfunction fieldByteLength(field: RadioMemoryMapField): number {\n if (field.type === 'u32') {\n return 4;\n }\n\n if (field.type === 'u16') {\n return 2;\n }\n\n if (field.type === 'bits') {\n return 0;\n }\n\n if (\n field.value?.kind === 'ascii' ||\n field.value?.kind === 'digits' ||\n field.value?.kind === 'dtmf' ||\n field.value?.kind === 'bbcd' ||\n field.value?.kind === 'lbcd'\n ) {\n return field.value.length;\n }\n\n if (field.value?.kind === 'tone') {\n return 2;\n }\n\n return 1;\n}\n\n/**\n * Chirp-style bitfields: declaration order is MSB → LSB within each byte.\n */\nclass BitfieldCursor {\n private radioAddress: number;\n private bitIndex = -1;\n private currentByte = 0;\n private dirty = false;\n\n constructor(\n private readonly contents: Uint8Array,\n private readonly memoryConfig: RadioMemoryConfig,\n startRadioAddress: number,\n ) {\n this.radioAddress = startRadioAddress;\n }\n\n private load(): void {\n if (this.bitIndex >= 0) {\n return;\n }\n\n this.currentByte = readByte(this.contents, bufferOffsetFor(this.radioAddress, this.memoryConfig, this.contents));\n this.bitIndex = 7;\n this.dirty = false;\n }\n\n private flushByte(): void {\n if (this.dirty) {\n writeByte(this.contents, bufferOffsetFor(this.radioAddress, this.memoryConfig, this.contents), this.currentByte);\n this.dirty = false;\n }\n }\n\n read(width: number): number {\n let result = 0;\n\n for (let bit = 0; bit < width; bit += 1) {\n this.load();\n result = (result << 1) | ((this.currentByte >> this.bitIndex) & 1);\n this.bitIndex -= 1;\n\n if (this.bitIndex < 0) {\n this.radioAddress += 1;\n }\n }\n\n return result;\n }\n\n write(width: number, value: number): void {\n for (let bit = width - 1; bit >= 0; bit -= 1) {\n this.load();\n const mask = 1 << this.bitIndex;\n\n if ((value >> bit) & 1) {\n this.currentByte |= mask;\n } else {\n this.currentByte &= ~mask;\n }\n\n this.dirty = true;\n this.bitIndex -= 1;\n\n if (this.bitIndex < 0) {\n this.flushByte();\n this.radioAddress += 1;\n }\n }\n }\n\n /**\n * Absolute radio address of the next unused byte after this bitfield group.\n */\n nextRadioAddress(): number {\n this.flushByte();\n\n if (this.bitIndex >= 0) {\n return this.radioAddress + 1;\n }\n\n return this.radioAddress;\n }\n}\n\nfunction decodeStruct(\n struct: RadioMemoryMapStruct,\n contents: Uint8Array,\n memoryConfig: RadioMemoryConfig,\n instanceIndex: number,\n): Record<string, RadioSettingValue> {\n const base = structInstanceAddress(struct, instanceIndex);\n const result: Record<string, RadioSettingValue> = {};\n let radioAddress = base;\n let bitCursor: BitfieldCursor | undefined;\n\n for (const field of struct.fields) {\n if (field.type === 'bits') {\n if (!bitCursor) {\n bitCursor = new BitfieldCursor(contents, memoryConfig, radioAddress);\n }\n\n const raw = bitCursor.read(field.width ?? 1);\n\n if (!field.reserved) {\n result[field.id] = decodeRawValue(field.value ?? { kind: 'integer' }, raw);\n }\n\n continue;\n }\n\n if (bitCursor) {\n radioAddress = bitCursor.nextRadioAddress();\n bitCursor = undefined;\n }\n\n const length = fieldByteLength(field);\n const bytes: number[] = [];\n\n for (let index = 0; index < length; index += 1) {\n bytes.push(readByte(contents, bufferOffsetFor(radioAddress + index, memoryConfig, contents)));\n }\n\n if (field.type === 'u16' || field.type === 'u32') {\n if (!field.reserved) {\n result[field.id] = decodeRawValue(field.value ?? { kind: 'integer' }, bytes);\n }\n } else if (!field.reserved) {\n result[field.id] = decodeRawValue(field.value, length === 1 ? bytes[0] : bytes);\n }\n\n radioAddress += length;\n }\n\n return result;\n}\n\nfunction encodeStruct(\n struct: RadioMemoryMapStruct,\n values: Record<string, RadioSettingValue>,\n contents: Uint8Array,\n memoryConfig: RadioMemoryConfig,\n instanceIndex: number,\n): void {\n const base = structInstanceAddress(struct, instanceIndex);\n let radioAddress = base;\n let bitCursor: BitfieldCursor | undefined;\n let bitStartAddress = base;\n\n for (const field of struct.fields) {\n if (field.type === 'bits') {\n if (!bitCursor) {\n bitStartAddress = radioAddress;\n bitCursor = new BitfieldCursor(contents, memoryConfig, bitStartAddress);\n }\n\n const width = field.width ?? 1;\n\n if (field.reserved) {\n // Advance by reading existing bits (preserves memory)\n bitCursor.read(width);\n } else {\n const encoded = encodeRawValue(field.value ?? { kind: 'integer' }, values[field.id] ?? 0, 1);\n bitCursor.write(width, encoded[0]);\n }\n\n continue;\n }\n\n if (bitCursor) {\n radioAddress = bitCursor.nextRadioAddress();\n bitCursor = undefined;\n }\n\n const length = fieldByteLength(field);\n\n if (field.reserved) {\n radioAddress += length;\n continue;\n }\n\n const settingValue = values[field.id];\n\n if (field.type === 'u16' || field.type === 'u32') {\n const encoded = encodeRawValue(field.value ?? { kind: 'integer' }, settingValue ?? 0, length);\n\n for (let index = 0; index < length; index += 1) {\n writeByte(contents, bufferOffsetFor(radioAddress + index, memoryConfig, contents), encoded[index] ?? 0);\n }\n } else {\n const encoded = encodeRawValue(field.value, settingValue ?? (field.value?.kind === 'ascii' ? '' : 0), length);\n\n for (let index = 0; index < length; index += 1) {\n writeByte(contents, bufferOffsetFor(radioAddress + index, memoryConfig, contents), encoded[index]);\n }\n }\n\n radioAddress += length;\n }\n\n if (bitCursor) {\n bitCursor.nextRadioAddress();\n }\n}\n\nfunction isStructInstanceEmpty(\n struct: RadioMemoryMapStruct,\n contents: Uint8Array,\n memoryConfig: RadioMemoryConfig,\n instanceIndex: number,\n): boolean {\n if (!struct.emptyWhen) {\n return false;\n }\n\n const base = structInstanceAddress(struct, instanceIndex);\n const firstByte = readByte(contents, bufferOffsetFor(base, memoryConfig, contents));\n return firstByte === struct.emptyWhen.equals;\n}\n\nfunction clearStructInstance(\n struct: RadioMemoryMapStruct,\n contents: Uint8Array,\n memoryConfig: RadioMemoryConfig,\n instanceIndex: number,\n): void {\n const base = structInstanceAddress(struct, instanceIndex);\n const length = struct.stride ?? 16;\n\n for (let index = 0; index < length; index += 1) {\n writeByte(contents, bufferOffsetFor(base + index, memoryConfig, contents), 0xff);\n }\n}\n\n/**\n * Decode radio-wide settings from a memory image using a JSON memory map.\n */\nexport function decodeMemoryMap(\n memoryMap: RadioMemoryMap,\n contents: Uint8Array,\n memoryConfig: RadioMemoryConfig,\n): RadioSettings {\n const settings: RadioSettings = {};\n\n for (const struct of memoryMap.structs) {\n const count = struct.count ?? 1;\n\n if (count > 1) {\n const items: RadioSettingValue[] = [];\n\n for (let index = 0; index < count; index += 1) {\n if (isStructInstanceEmpty(struct, contents, memoryConfig, index)) {\n items.push(null);\n } else {\n items.push(decodeStruct(struct, contents, memoryConfig, index));\n }\n }\n\n settings[struct.id] = items;\n } else {\n settings[struct.id] = decodeStruct(struct, contents, memoryConfig, 0);\n }\n }\n\n return settings;\n}\n\n/**\n * Encode radio-wide settings into an existing memory image in place.\n * Returns the same buffer for convenience.\n */\nexport function encodeMemoryMap(\n memoryMap: RadioMemoryMap,\n settings: RadioSettings,\n contents: Uint8Array,\n memoryConfig: RadioMemoryConfig,\n): Uint8Array {\n for (const struct of memoryMap.structs) {\n const count = struct.count ?? 1;\n const value = settings[struct.id];\n\n if (count > 1) {\n const items = Array.isArray(value) ? value : [];\n\n for (let index = 0; index < count; index += 1) {\n const item = items[index];\n\n if (item === null || item === undefined) {\n if (struct.clearEmpty) {\n clearStructInstance(struct, contents, memoryConfig, index);\n }\n\n continue;\n }\n\n const record =\n item && typeof item === 'object' && !Array.isArray(item) ? (item as Record<string, RadioSettingValue>) : {};\n encodeStruct(struct, record, contents, memoryConfig, index);\n }\n } else {\n const record =\n value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, RadioSettingValue>) : {};\n encodeStruct(struct, record, contents, memoryConfig, 0);\n }\n }\n\n return contents;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"memory-map-codec.js","sourceRoot":"","sources":["../../src/memory/memory-map-codec.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n RadioMemoryConfig,\n RadioMemoryMap,\n RadioMemoryMapField,\n RadioMemoryMapStruct,\n RadioMemoryMapValueKind,\n RadioSettings,\n RadioSettingValue,\n} from '@springfield/ham-radio-api';\n\nconst DEFAULT_DTMF_CHARSET = '0123456789 *#ABCD';\n\n/**\n * Parse a seek address from a number or hex/decimal string.\n */\nexport function parseSeekAddress(seek: number | string): number {\n if (typeof seek === 'number') {\n return seek;\n }\n\n const trimmed = seek.trim();\n\n if (/^0x/i.test(trimmed)) {\n return Number.parseInt(trimmed, 16);\n }\n\n return Number.parseInt(trimmed, 10);\n}\n\n/**\n * Map a radio EEPROM address to an offset in the memory buffer.\n *\n * Packed buffers (driver read) concatenate segments in config order.\n * Sparse buffers that cover the highest segment end address use absolute offsets.\n */\nexport function radioAddressToBufferOffset(\n radioAddress: number,\n memoryConfig: RadioMemoryConfig,\n bufferLength: number,\n): number {\n const segments = Object.values(memoryConfig.segments);\n const maxEndAddress = Math.max(...segments.map((segment) => segment.endAddress));\n\n if (bufferLength >= maxEndAddress + 1) {\n return radioAddress;\n }\n\n let offset = 0;\n\n for (const segment of segments) {\n if (radioAddress >= segment.startAddress && radioAddress <= segment.endAddress) {\n return offset + (radioAddress - segment.startAddress);\n }\n\n offset += segment.endAddress - segment.startAddress + 1;\n }\n\n throw new Error(`Radio address 0x${radioAddress.toString(16)} is not in any memory segment`);\n}\n\nfunction bufferOffsetFor(radioAddress: number, memoryConfig: RadioMemoryConfig, contents: Uint8Array): number {\n return radioAddressToBufferOffset(radioAddress, memoryConfig, contents.length);\n}\n\nfunction readByte(contents: Uint8Array, offset: number): number {\n if (offset < 0 || offset >= contents.length) {\n throw new RangeError(`Memory-map read out of bounds at offset ${offset}`);\n }\n\n return contents[offset];\n}\n\nfunction writeByte(contents: Uint8Array, offset: number, value: number): void {\n if (offset < 0 || offset >= contents.length) {\n throw new RangeError(`Memory-map write out of bounds at offset ${offset}`);\n }\n\n contents[offset] = value & 0xff;\n}\n\nfunction decodeInteger(raw: number | number[]): number {\n if (typeof raw === 'number') {\n return raw;\n }\n\n let value = 0;\n\n for (let index = 0; index < raw.length; index += 1) {\n value += raw[index] * 256 ** index;\n }\n\n return value;\n}\n\nfunction encodeInteger(value: RadioSettingValue, byteLength: number): number[] {\n let numeric = typeof value === 'number' ? value : 0;\n const bytes: number[] = [];\n\n for (let index = 0; index < byteLength; index += 1) {\n bytes.push(numeric & 0xff);\n numeric = Math.floor(numeric / 256);\n }\n\n return bytes;\n}\n\n/**\n * Radio address of repeated-struct instance `index`, including Kenwood-style\n * grouped stride (N records + pad bytes per clone block).\n */\nexport function structInstanceAddress(struct: RadioMemoryMapStruct, instanceIndex: number): number {\n const seek = parseSeekAddress(struct.seek);\n const stride = struct.stride ?? 0;\n const groupSize = struct.groupSize;\n\n if (!groupSize) {\n return seek + instanceIndex * stride;\n }\n\n const groupPad = struct.groupPad ?? 0;\n const groupIndex = Math.floor(instanceIndex / groupSize);\n const indexInGroup = instanceIndex % groupSize;\n\n return seek + groupIndex * (stride * groupSize + groupPad) + indexInGroup * stride;\n}\n\n/**\n * ASCII fields stop at NUL or 0xFF, then drop trailing spaces. The bytes after\n * the visible text are not part of the setting value.\n */\nfunction decodeAscii(bytes: readonly number[]): string {\n let text = '';\n\n for (const byte of bytes) {\n if (byte === 0xff || byte === 0x00) {\n break;\n }\n\n text += String.fromCodePoint(byte);\n }\n\n return text.trimEnd();\n}\n\n/**\n * Clone images mix space, NUL, and 0xFF padding in one ASCII field. Re-encoding\n * an unchanged value with the single declared pad rewrites those bytes (a\n * TH-D74 power-on message ending in spaces came back NUL-padded; a D-STAR memo\n * ending in NUL came back space-padded). When the text still matches what\n * decode would return, keep the stored bytes. Edited text uses `kind.pad`.\n */\nfunction encodeAscii(\n kind: Extract<RadioMemoryMapValueKind, { kind: 'ascii' }>,\n value: RadioSettingValue,\n byteLength: number,\n originalBytes: readonly number[] | undefined,\n): number[] {\n const text = typeof value === 'string' ? value : '';\n\n if (originalBytes && originalBytes.length === byteLength && decodeAscii(originalBytes) === text) {\n return [...originalBytes];\n }\n\n const pad = kind.pad ?? 0xff;\n const bytes: number[] = [];\n\n for (let index = 0; index < byteLength; index += 1) {\n bytes[index] = index < text.length ? (text.codePointAt(index) ?? pad) : pad;\n }\n\n return bytes;\n}\n\nfunction decodeRawValue(kind: RadioMemoryMapValueKind | undefined, raw: number | number[]): RadioSettingValue {\n if (!kind) {\n return typeof raw === 'number' ? raw : raw;\n }\n\n switch (kind.kind) {\n case 'integer':\n return decodeInteger(raw);\n case 'boolean':\n return (typeof raw === 'number' ? raw : raw[0]) !== 0;\n case 'enum': {\n const index = typeof raw === 'number' ? raw : raw[0];\n return kind.values[index] ?? kind.values[0] ?? '';\n }\n case 'ascii': {\n const bytes = typeof raw === 'number' ? [raw] : raw;\n return decodeAscii(bytes);\n }\n case 'digits': {\n const bytes = typeof raw === 'number' ? [raw] : raw;\n let value = 0;\n\n for (const digit of bytes) {\n value = value * 10 + (digit & 0x0f);\n }\n\n return value * (kind.scale ?? 1);\n }\n case 'dtmf': {\n const bytes = typeof raw === 'number' ? [raw] : raw;\n const charset = kind.charset ?? DEFAULT_DTMF_CHARSET;\n let text = '';\n\n for (const byte of bytes) {\n if (byte >= 0x1f) {\n break;\n }\n\n if (byte < charset.length) {\n text += charset[byte];\n }\n }\n\n return text;\n }\n case 'bbcd': {\n const bytes = typeof raw === 'number' ? [raw] : raw;\n let value = 0;\n\n for (const byte of bytes) {\n value = value * 100 + ((byte >> 4) & 0x0f) * 10 + (byte & 0x0f);\n }\n\n return value;\n }\n case 'lbcd': {\n const bytes = typeof raw === 'number' ? [raw] : raw;\n let word = 0;\n\n for (let index = 0; index < bytes.length; index += 1) {\n word |= bytes[index] << (8 * index);\n }\n\n if (word === 0xff_ff_ff_ff || bytes.every((byte) => byte === 0xff)) {\n return null;\n }\n\n return Number.parseInt(word.toString(16), 10) * (kind.scale ?? 10);\n }\n case 'tone': {\n const rawValue = typeof raw === 'number' ? raw : raw[0] | (raw[1] << 8);\n const ctcssMin = kind.ctcssMin ?? 0x0258;\n const reverseOffset = kind.reverseOffset ?? 0x69;\n\n if (rawValue === 0 || rawValue === 0xffff) {\n return { mode: 'none' };\n }\n\n if (rawValue >= ctcssMin) {\n return { mode: 'ctcss', value: rawValue };\n }\n\n let index = rawValue;\n let polarity: 'N' | 'R' = 'N';\n\n if (rawValue > reverseOffset) {\n index = rawValue - reverseOffset;\n polarity = 'R';\n } else {\n index = rawValue - 1;\n }\n\n const code = kind.values[index] ?? 0;\n return { mode: 'dcs', code, polarity };\n }\n case 'ctcss-index': {\n const index = decodeInteger(raw);\n const tenths = kind.values[index] ?? kind.values[0] ?? 0;\n return { mode: 'ctcss', value: tenths };\n }\n case 'dcs-index': {\n const index = decodeInteger(raw);\n const code = kind.values[index] ?? 0;\n return { mode: 'dcs', code, polarity: 'N' };\n }\n default: {\n const exhaustive: never = kind;\n return exhaustive;\n }\n }\n}\n\nfunction encodeRawValue(\n kind: RadioMemoryMapValueKind | undefined,\n value: RadioSettingValue,\n byteLength: number,\n originalBytes?: readonly number[],\n): number[] {\n const bytes = Array.from({ length: byteLength }, () => 0xff);\n\n if (!kind) {\n if (typeof value === 'number') {\n bytes[0] = value & 0xff;\n }\n\n return bytes;\n }\n\n switch (kind.kind) {\n case 'integer': {\n return encodeInteger(value, byteLength);\n }\n case 'boolean': {\n bytes[0] = value ? 1 : 0;\n return bytes;\n }\n case 'enum': {\n const index = typeof value === 'string' ? kind.values.indexOf(value) : -1;\n bytes[0] = index >= 0 ? index : 0;\n return bytes;\n }\n case 'ascii':\n return encodeAscii(kind, value, byteLength, originalBytes);\n case 'digits': {\n const scale = kind.scale ?? 1;\n let numeric = typeof value === 'number' ? Math.round(value / scale) : 0;\n\n for (let index = byteLength - 1; index >= 0; index -= 1) {\n bytes[index] = numeric % 10;\n numeric = Math.floor(numeric / 10);\n }\n\n return bytes;\n }\n case 'dtmf': {\n const text = typeof value === 'string' ? value : '';\n const charset = kind.charset ?? DEFAULT_DTMF_CHARSET;\n\n for (let index = 0; index < byteLength; index += 1) {\n if (index < text.length) {\n const charIndex = charset.indexOf(text[index]);\n bytes[index] = charIndex >= 0 ? charIndex : 0xff;\n } else {\n bytes[index] = 0xff;\n }\n }\n\n return bytes;\n }\n case 'bbcd': {\n let numeric = typeof value === 'number' ? value : 0;\n const digits: number[] = [];\n\n for (let index = 0; index < byteLength * 2; index += 1) {\n digits.unshift(numeric % 10);\n numeric = Math.floor(numeric / 10);\n }\n\n for (let index = 0; index < byteLength; index += 1) {\n bytes[index] = ((digits[index * 2] & 0x0f) << 4) | (digits[index * 2 + 1] & 0x0f);\n }\n\n return bytes;\n }\n case 'lbcd': {\n if (value === null || value === undefined) {\n return Array.from({ length: byteLength }, () => 0xff);\n }\n\n const scale = kind.scale ?? 10;\n const hz = typeof value === 'number' ? value : 0;\n let word = Number.parseInt(Math.round(hz / scale).toString(10), 16);\n\n for (let index = 0; index < byteLength; index += 1) {\n bytes[index] = word & 0xff;\n word >>= 8;\n }\n\n return bytes;\n }\n case 'tone': {\n const ctcssMin = kind.ctcssMin ?? 0x0258;\n const reverseOffset = kind.reverseOffset ?? 0x69;\n let rawValue = 0;\n\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const tone = value as { mode?: string; value?: number; code?: number; polarity?: string };\n\n if (tone.mode === 'ctcss' && typeof tone.value === 'number') {\n rawValue = tone.value;\n } else if (tone.mode === 'dcs' && typeof tone.code === 'number') {\n const index = kind.values.indexOf(tone.code);\n rawValue = index >= 0 ? index + 1 : 0;\n\n if (tone.polarity === 'R') {\n rawValue += reverseOffset;\n }\n }\n }\n\n if (rawValue >= ctcssMin || rawValue === 0) {\n // CTCSS or none: write LE u16\n }\n\n bytes[0] = rawValue & 0xff;\n bytes[1] = (rawValue >> 8) & 0xff;\n return bytes;\n }\n case 'ctcss-index': {\n const tone = value && typeof value === 'object' && !Array.isArray(value) ? (value as { mode?: string; value?: number }) : undefined;\n const tenths = tone?.mode === 'ctcss' && typeof tone.value === 'number' ? tone.value : 0;\n const index = kind.values.indexOf(tenths);\n return encodeInteger(index >= 0 ? index : 0, byteLength);\n }\n case 'dcs-index': {\n const tone = value && typeof value === 'object' && !Array.isArray(value) ? (value as { mode?: string; code?: number }) : undefined;\n const code = tone?.mode === 'dcs' && typeof tone.code === 'number' ? tone.code : 0;\n const index = kind.values.indexOf(code);\n return encodeInteger(index >= 0 ? index : 0, byteLength);\n }\n default: {\n const exhaustive: never = kind;\n return exhaustive;\n }\n }\n}\n\nfunction fieldByteLength(field: RadioMemoryMapField): number {\n if (field.type === 'u32') {\n return 4;\n }\n\n if (field.type === 'u16') {\n return 2;\n }\n\n if (field.type === 'bits') {\n return 0;\n }\n\n if (\n field.value?.kind === 'ascii' ||\n field.value?.kind === 'digits' ||\n field.value?.kind === 'dtmf' ||\n field.value?.kind === 'bbcd' ||\n field.value?.kind === 'lbcd'\n ) {\n return field.value.length;\n }\n\n if (field.value?.kind === 'tone') {\n return 2;\n }\n\n return 1;\n}\n\n/**\n * Chirp-style bitfields: declaration order is MSB → LSB within each byte.\n */\nclass BitfieldCursor {\n private radioAddress: number;\n private bitIndex = -1;\n private currentByte = 0;\n private dirty = false;\n\n constructor(\n private readonly contents: Uint8Array,\n private readonly memoryConfig: RadioMemoryConfig,\n startRadioAddress: number,\n ) {\n this.radioAddress = startRadioAddress;\n }\n\n private load(): void {\n if (this.bitIndex >= 0) {\n return;\n }\n\n this.currentByte = readByte(this.contents, bufferOffsetFor(this.radioAddress, this.memoryConfig, this.contents));\n this.bitIndex = 7;\n this.dirty = false;\n }\n\n private flushByte(): void {\n if (this.dirty) {\n writeByte(this.contents, bufferOffsetFor(this.radioAddress, this.memoryConfig, this.contents), this.currentByte);\n this.dirty = false;\n }\n }\n\n read(width: number): number {\n let result = 0;\n\n for (let bit = 0; bit < width; bit += 1) {\n this.load();\n result = (result << 1) | ((this.currentByte >> this.bitIndex) & 1);\n this.bitIndex -= 1;\n\n if (this.bitIndex < 0) {\n this.radioAddress += 1;\n }\n }\n\n return result;\n }\n\n write(width: number, value: number): void {\n for (let bit = width - 1; bit >= 0; bit -= 1) {\n this.load();\n const mask = 1 << this.bitIndex;\n\n if ((value >> bit) & 1) {\n this.currentByte |= mask;\n } else {\n this.currentByte &= ~mask;\n }\n\n this.dirty = true;\n this.bitIndex -= 1;\n\n if (this.bitIndex < 0) {\n this.flushByte();\n this.radioAddress += 1;\n }\n }\n }\n\n /**\n * Absolute radio address of the next unused byte after this bitfield group.\n */\n nextRadioAddress(): number {\n this.flushByte();\n\n if (this.bitIndex >= 0) {\n return this.radioAddress + 1;\n }\n\n return this.radioAddress;\n }\n}\n\nfunction decodeStruct(\n struct: RadioMemoryMapStruct,\n contents: Uint8Array,\n memoryConfig: RadioMemoryConfig,\n instanceIndex: number,\n): Record<string, RadioSettingValue> {\n const base = structInstanceAddress(struct, instanceIndex);\n const result: Record<string, RadioSettingValue> = {};\n let radioAddress = base;\n let bitCursor: BitfieldCursor | undefined;\n\n for (const field of struct.fields) {\n if (field.type === 'bits') {\n if (!bitCursor) {\n bitCursor = new BitfieldCursor(contents, memoryConfig, radioAddress);\n }\n\n const raw = bitCursor.read(field.width ?? 1);\n\n if (!field.reserved) {\n result[field.id] = decodeRawValue(field.value ?? { kind: 'integer' }, raw);\n }\n\n continue;\n }\n\n if (bitCursor) {\n radioAddress = bitCursor.nextRadioAddress();\n bitCursor = undefined;\n }\n\n const length = fieldByteLength(field);\n const bytes: number[] = [];\n\n for (let index = 0; index < length; index += 1) {\n bytes.push(readByte(contents, bufferOffsetFor(radioAddress + index, memoryConfig, contents)));\n }\n\n if (field.type === 'u16' || field.type === 'u32') {\n if (!field.reserved) {\n result[field.id] = decodeRawValue(field.value ?? { kind: 'integer' }, bytes);\n }\n } else if (!field.reserved) {\n result[field.id] = decodeRawValue(field.value, length === 1 ? bytes[0] : bytes);\n }\n\n radioAddress += length;\n }\n\n return result;\n}\n\nfunction encodeStruct(\n struct: RadioMemoryMapStruct,\n values: Record<string, RadioSettingValue>,\n contents: Uint8Array,\n memoryConfig: RadioMemoryConfig,\n instanceIndex: number,\n): void {\n const base = structInstanceAddress(struct, instanceIndex);\n let radioAddress = base;\n let bitCursor: BitfieldCursor | undefined;\n let bitStartAddress = base;\n\n for (const field of struct.fields) {\n if (field.type === 'bits') {\n if (!bitCursor) {\n bitStartAddress = radioAddress;\n bitCursor = new BitfieldCursor(contents, memoryConfig, bitStartAddress);\n }\n\n const width = field.width ?? 1;\n\n if (field.reserved) {\n // Advance by reading existing bits (preserves memory)\n bitCursor.read(width);\n } else {\n const encoded = encodeRawValue(field.value ?? { kind: 'integer' }, values[field.id] ?? 0, 1);\n bitCursor.write(width, encoded[0]);\n }\n\n continue;\n }\n\n if (bitCursor) {\n radioAddress = bitCursor.nextRadioAddress();\n bitCursor = undefined;\n }\n\n const length = fieldByteLength(field);\n\n if (field.reserved) {\n radioAddress += length;\n continue;\n }\n\n const settingValue = values[field.id];\n const originalBytes: number[] | undefined = field.value?.kind === 'ascii' ? [] : undefined;\n\n if (originalBytes) {\n for (let index = 0; index < length; index += 1) {\n originalBytes.push(readByte(contents, bufferOffsetFor(radioAddress + index, memoryConfig, contents)));\n }\n }\n\n if (field.type === 'u16' || field.type === 'u32') {\n const encoded = encodeRawValue(field.value ?? { kind: 'integer' }, settingValue ?? 0, length, originalBytes);\n\n for (let index = 0; index < length; index += 1) {\n writeByte(contents, bufferOffsetFor(radioAddress + index, memoryConfig, contents), encoded[index] ?? 0);\n }\n } else {\n const encoded = encodeRawValue(\n field.value,\n settingValue ?? (field.value?.kind === 'ascii' ? '' : 0),\n length,\n originalBytes,\n );\n\n for (let index = 0; index < length; index += 1) {\n writeByte(contents, bufferOffsetFor(radioAddress + index, memoryConfig, contents), encoded[index]);\n }\n }\n\n radioAddress += length;\n }\n\n if (bitCursor) {\n bitCursor.nextRadioAddress();\n }\n}\n\nfunction isStructInstanceEmpty(\n struct: RadioMemoryMapStruct,\n contents: Uint8Array,\n memoryConfig: RadioMemoryConfig,\n instanceIndex: number,\n): boolean {\n if (!struct.emptyWhen) {\n return false;\n }\n\n const base = structInstanceAddress(struct, instanceIndex);\n const firstByte = readByte(contents, bufferOffsetFor(base, memoryConfig, contents));\n return firstByte === struct.emptyWhen.equals;\n}\n\nfunction clearStructInstance(\n struct: RadioMemoryMapStruct,\n contents: Uint8Array,\n memoryConfig: RadioMemoryConfig,\n instanceIndex: number,\n): void {\n const base = structInstanceAddress(struct, instanceIndex);\n const length = struct.stride ?? 16;\n\n for (let index = 0; index < length; index += 1) {\n writeByte(contents, bufferOffsetFor(base + index, memoryConfig, contents), 0xff);\n }\n}\n\n/**\n * Decode radio-wide settings from a memory image using a JSON memory map.\n */\nexport function decodeMemoryMap(\n memoryMap: RadioMemoryMap,\n contents: Uint8Array,\n memoryConfig: RadioMemoryConfig,\n): RadioSettings {\n const settings: RadioSettings = {};\n\n for (const struct of memoryMap.structs) {\n const count = struct.count ?? 1;\n\n if (count > 1) {\n const items: RadioSettingValue[] = [];\n\n for (let index = 0; index < count; index += 1) {\n if (isStructInstanceEmpty(struct, contents, memoryConfig, index)) {\n items.push(null);\n } else {\n items.push(decodeStruct(struct, contents, memoryConfig, index));\n }\n }\n\n settings[struct.id] = items;\n } else {\n settings[struct.id] = decodeStruct(struct, contents, memoryConfig, 0);\n }\n }\n\n return settings;\n}\n\n/**\n * Encode radio-wide settings into an existing memory image in place.\n * Returns the same buffer for convenience.\n */\nexport function encodeMemoryMap(\n memoryMap: RadioMemoryMap,\n settings: RadioSettings,\n contents: Uint8Array,\n memoryConfig: RadioMemoryConfig,\n): Uint8Array {\n for (const struct of memoryMap.structs) {\n const count = struct.count ?? 1;\n const value = settings[struct.id];\n\n if (count > 1) {\n const items = Array.isArray(value) ? value : [];\n\n for (let index = 0; index < count; index += 1) {\n const item = items[index];\n\n if (item === null || item === undefined) {\n if (struct.clearEmpty) {\n clearStructInstance(struct, contents, memoryConfig, index);\n }\n\n continue;\n }\n\n const record =\n item && typeof item === 'object' && !Array.isArray(item) ? (item as Record<string, RadioSettingValue>) : {};\n encodeStruct(struct, record, contents, memoryConfig, index);\n }\n } else {\n const record =\n value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, RadioSettingValue>) : {};\n encodeStruct(struct, record, contents, memoryConfig, 0);\n }\n }\n\n return contents;\n}\n"]}
|
|
@@ -213,9 +213,34 @@
|
|
|
213
213
|
"items": {
|
|
214
214
|
"$ref": "#/definitions/protocolStep"
|
|
215
215
|
}
|
|
216
|
+
},
|
|
217
|
+
"onError": {
|
|
218
|
+
"$ref": "#/definitions/onError",
|
|
219
|
+
"description": "Optional cleanup exchanges run after a read or write failure (or cancel)"
|
|
216
220
|
}
|
|
217
221
|
},
|
|
218
222
|
"definitions": {
|
|
223
|
+
"onError": {
|
|
224
|
+
"type": "object",
|
|
225
|
+
"description": "Per-operation cleanup steps. Items are send/expect exchanges only; no chunked read/write or CAT loops.",
|
|
226
|
+
"additionalProperties": false,
|
|
227
|
+
"properties": {
|
|
228
|
+
"readMemory": {
|
|
229
|
+
"type": "array",
|
|
230
|
+
"description": "Cleanup exchanges after a failed or cancelled readMemory",
|
|
231
|
+
"items": {
|
|
232
|
+
"$ref": "#/definitions/exchangeStep"
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
"writeMemory": {
|
|
236
|
+
"type": "array",
|
|
237
|
+
"description": "Cleanup exchanges after a failed or cancelled writeMemory",
|
|
238
|
+
"items": {
|
|
239
|
+
"$ref": "#/definitions/exchangeStep"
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
},
|
|
219
244
|
"protocolStep": {
|
|
220
245
|
"description": "A single protocol step: an exchange, a chunked read, a chunked write, or a live CAT memory loop",
|
|
221
246
|
"oneOf": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@springfield/ham-radio-utils",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.16.1",
|
|
4
4
|
"author": "Bryan Hunt",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"readme": "README.md",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"test:unit": "vitest run test/unit"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@springfield/ham-radio-api": "^18.0.
|
|
33
|
+
"@springfield/ham-radio-api": "^18.0.2",
|
|
34
34
|
"ajv": "^8.20.0",
|
|
35
35
|
"ajv-formats": "^3.0.1",
|
|
36
36
|
"fishery": "^2.4.0",
|
|
@@ -44,15 +44,15 @@
|
|
|
44
44
|
"@semantic-release/git": "11.0.1",
|
|
45
45
|
"@semantic-release/github": "12.0.10",
|
|
46
46
|
"@semantic-release/npm": "13.2.0",
|
|
47
|
-
"@types/node": "26.6.
|
|
47
|
+
"@types/node": "26.6.5",
|
|
48
48
|
"@types/sprintf-js": "1.1.4",
|
|
49
49
|
"husky": "9.1.7",
|
|
50
|
-
"oxfmt": "^0.
|
|
51
|
-
"oxlint": "1.
|
|
50
|
+
"oxfmt": "^0.73.0",
|
|
51
|
+
"oxlint": "1.88.0",
|
|
52
52
|
"semantic-release": "25.0.9",
|
|
53
53
|
"typescript": "7.0.2",
|
|
54
|
-
"vite": "^8.3.
|
|
55
|
-
"vitest": "^5.0.
|
|
54
|
+
"vite": "^8.3.4",
|
|
55
|
+
"vitest": "^5.0.5"
|
|
56
56
|
},
|
|
57
57
|
"resolutions": {
|
|
58
58
|
"conventional-changelog-conventionalcommits": "9.3.1"
|
|
@@ -124,6 +124,53 @@ export function structInstanceAddress(struct: RadioMemoryMapStruct, instanceInde
|
|
|
124
124
|
return seek + groupIndex * (stride * groupSize + groupPad) + indexInGroup * stride;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
+
/**
|
|
128
|
+
* ASCII fields stop at NUL or 0xFF, then drop trailing spaces. The bytes after
|
|
129
|
+
* the visible text are not part of the setting value.
|
|
130
|
+
*/
|
|
131
|
+
function decodeAscii(bytes: readonly number[]): string {
|
|
132
|
+
let text = '';
|
|
133
|
+
|
|
134
|
+
for (const byte of bytes) {
|
|
135
|
+
if (byte === 0xff || byte === 0x00) {
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
text += String.fromCodePoint(byte);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return text.trimEnd();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Clone images mix space, NUL, and 0xFF padding in one ASCII field. Re-encoding
|
|
147
|
+
* an unchanged value with the single declared pad rewrites those bytes (a
|
|
148
|
+
* TH-D74 power-on message ending in spaces came back NUL-padded; a D-STAR memo
|
|
149
|
+
* ending in NUL came back space-padded). When the text still matches what
|
|
150
|
+
* decode would return, keep the stored bytes. Edited text uses `kind.pad`.
|
|
151
|
+
*/
|
|
152
|
+
function encodeAscii(
|
|
153
|
+
kind: Extract<RadioMemoryMapValueKind, { kind: 'ascii' }>,
|
|
154
|
+
value: RadioSettingValue,
|
|
155
|
+
byteLength: number,
|
|
156
|
+
originalBytes: readonly number[] | undefined,
|
|
157
|
+
): number[] {
|
|
158
|
+
const text = typeof value === 'string' ? value : '';
|
|
159
|
+
|
|
160
|
+
if (originalBytes && originalBytes.length === byteLength && decodeAscii(originalBytes) === text) {
|
|
161
|
+
return [...originalBytes];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const pad = kind.pad ?? 0xff;
|
|
165
|
+
const bytes: number[] = [];
|
|
166
|
+
|
|
167
|
+
for (let index = 0; index < byteLength; index += 1) {
|
|
168
|
+
bytes[index] = index < text.length ? (text.codePointAt(index) ?? pad) : pad;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return bytes;
|
|
172
|
+
}
|
|
173
|
+
|
|
127
174
|
function decodeRawValue(kind: RadioMemoryMapValueKind | undefined, raw: number | number[]): RadioSettingValue {
|
|
128
175
|
if (!kind) {
|
|
129
176
|
return typeof raw === 'number' ? raw : raw;
|
|
@@ -140,17 +187,7 @@ function decodeRawValue(kind: RadioMemoryMapValueKind | undefined, raw: number |
|
|
|
140
187
|
}
|
|
141
188
|
case 'ascii': {
|
|
142
189
|
const bytes = typeof raw === 'number' ? [raw] : raw;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
for (const byte of bytes) {
|
|
146
|
-
if (byte === 0xff || byte === 0x00) {
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
text += String.fromCodePoint(byte);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return text.trimEnd();
|
|
190
|
+
return decodeAscii(bytes);
|
|
154
191
|
}
|
|
155
192
|
case 'digits': {
|
|
156
193
|
const bytes = typeof raw === 'number' ? [raw] : raw;
|
|
@@ -246,7 +283,12 @@ function decodeRawValue(kind: RadioMemoryMapValueKind | undefined, raw: number |
|
|
|
246
283
|
}
|
|
247
284
|
}
|
|
248
285
|
|
|
249
|
-
function encodeRawValue(
|
|
286
|
+
function encodeRawValue(
|
|
287
|
+
kind: RadioMemoryMapValueKind | undefined,
|
|
288
|
+
value: RadioSettingValue,
|
|
289
|
+
byteLength: number,
|
|
290
|
+
originalBytes?: readonly number[],
|
|
291
|
+
): number[] {
|
|
250
292
|
const bytes = Array.from({ length: byteLength }, () => 0xff);
|
|
251
293
|
|
|
252
294
|
if (!kind) {
|
|
@@ -270,16 +312,8 @@ function encodeRawValue(kind: RadioMemoryMapValueKind | undefined, value: RadioS
|
|
|
270
312
|
bytes[0] = index >= 0 ? index : 0;
|
|
271
313
|
return bytes;
|
|
272
314
|
}
|
|
273
|
-
case 'ascii':
|
|
274
|
-
|
|
275
|
-
const pad = kind.pad ?? 0xff;
|
|
276
|
-
|
|
277
|
-
for (let index = 0; index < byteLength; index += 1) {
|
|
278
|
-
bytes[index] = index < text.length ? (text.codePointAt(index) ?? pad) : pad;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return bytes;
|
|
282
|
-
}
|
|
315
|
+
case 'ascii':
|
|
316
|
+
return encodeAscii(kind, value, byteLength, originalBytes);
|
|
283
317
|
case 'digits': {
|
|
284
318
|
const scale = kind.scale ?? 1;
|
|
285
319
|
let numeric = typeof value === 'number' ? Math.round(value / scale) : 0;
|
|
@@ -596,15 +630,27 @@ function encodeStruct(
|
|
|
596
630
|
}
|
|
597
631
|
|
|
598
632
|
const settingValue = values[field.id];
|
|
633
|
+
const originalBytes: number[] | undefined = field.value?.kind === 'ascii' ? [] : undefined;
|
|
634
|
+
|
|
635
|
+
if (originalBytes) {
|
|
636
|
+
for (let index = 0; index < length; index += 1) {
|
|
637
|
+
originalBytes.push(readByte(contents, bufferOffsetFor(radioAddress + index, memoryConfig, contents)));
|
|
638
|
+
}
|
|
639
|
+
}
|
|
599
640
|
|
|
600
641
|
if (field.type === 'u16' || field.type === 'u32') {
|
|
601
|
-
const encoded = encodeRawValue(field.value ?? { kind: 'integer' }, settingValue ?? 0, length);
|
|
642
|
+
const encoded = encodeRawValue(field.value ?? { kind: 'integer' }, settingValue ?? 0, length, originalBytes);
|
|
602
643
|
|
|
603
644
|
for (let index = 0; index < length; index += 1) {
|
|
604
645
|
writeByte(contents, bufferOffsetFor(radioAddress + index, memoryConfig, contents), encoded[index] ?? 0);
|
|
605
646
|
}
|
|
606
647
|
} else {
|
|
607
|
-
const encoded = encodeRawValue(
|
|
648
|
+
const encoded = encodeRawValue(
|
|
649
|
+
field.value,
|
|
650
|
+
settingValue ?? (field.value?.kind === 'ascii' ? '' : 0),
|
|
651
|
+
length,
|
|
652
|
+
originalBytes,
|
|
653
|
+
);
|
|
608
654
|
|
|
609
655
|
for (let index = 0; index < length; index += 1) {
|
|
610
656
|
writeByte(contents, bufferOffsetFor(radioAddress + index, memoryConfig, contents), encoded[index]);
|
|
@@ -213,9 +213,34 @@
|
|
|
213
213
|
"items": {
|
|
214
214
|
"$ref": "#/definitions/protocolStep"
|
|
215
215
|
}
|
|
216
|
+
},
|
|
217
|
+
"onError": {
|
|
218
|
+
"$ref": "#/definitions/onError",
|
|
219
|
+
"description": "Optional cleanup exchanges run after a read or write failure (or cancel)"
|
|
216
220
|
}
|
|
217
221
|
},
|
|
218
222
|
"definitions": {
|
|
223
|
+
"onError": {
|
|
224
|
+
"type": "object",
|
|
225
|
+
"description": "Per-operation cleanup steps. Items are send/expect exchanges only; no chunked read/write or CAT loops.",
|
|
226
|
+
"additionalProperties": false,
|
|
227
|
+
"properties": {
|
|
228
|
+
"readMemory": {
|
|
229
|
+
"type": "array",
|
|
230
|
+
"description": "Cleanup exchanges after a failed or cancelled readMemory",
|
|
231
|
+
"items": {
|
|
232
|
+
"$ref": "#/definitions/exchangeStep"
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
"writeMemory": {
|
|
236
|
+
"type": "array",
|
|
237
|
+
"description": "Cleanup exchanges after a failed or cancelled writeMemory",
|
|
238
|
+
"items": {
|
|
239
|
+
"$ref": "#/definitions/exchangeStep"
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
},
|
|
219
244
|
"protocolStep": {
|
|
220
245
|
"description": "A single protocol step: an exchange, a chunked read, a chunked write, or a live CAT memory loop",
|
|
221
246
|
"oneOf": [
|