@k67/kaitai-struct-ts 0.8.0 → 0.10.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/dist/browser/index.mjs +272 -0
- package/dist/browser/index.mjs.map +1 -0
- package/dist/cli.js +1614 -923
- package/dist/index.d.mts +70 -7
- package/dist/index.d.ts +70 -7
- package/dist/index.js +704 -46
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +704 -46
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -3
package/dist/index.mjs
CHANGED
@@ -1,29 +1,67 @@
|
|
1
1
|
// src/utils/errors.ts
|
2
2
|
var KaitaiError = class _KaitaiError extends Error {
|
3
|
-
constructor(message, position) {
|
4
|
-
super(message);
|
3
|
+
constructor(message, position, context) {
|
4
|
+
super(_KaitaiError.formatMessage(message, position, context));
|
5
5
|
this.position = position;
|
6
|
+
this.context = context;
|
6
7
|
this.name = "KaitaiError";
|
7
8
|
Object.setPrototypeOf(this, _KaitaiError.prototype);
|
8
9
|
}
|
10
|
+
/**
|
11
|
+
* Format error message with position and context.
|
12
|
+
* @private
|
13
|
+
*/
|
14
|
+
static formatMessage(message, position, context) {
|
15
|
+
let formatted = message;
|
16
|
+
if (position !== void 0) {
|
17
|
+
formatted += ` (at byte offset 0x${position.toString(16).toUpperCase()})`;
|
18
|
+
}
|
19
|
+
if (context && context.length > 0) {
|
20
|
+
const hexContext = _KaitaiError.formatHexContext(context, position);
|
21
|
+
formatted += `
|
22
|
+
${hexContext}`;
|
23
|
+
}
|
24
|
+
return formatted;
|
25
|
+
}
|
26
|
+
/**
|
27
|
+
* Format hex dump context around error position.
|
28
|
+
* @private
|
29
|
+
*/
|
30
|
+
static formatHexContext(data, position) {
|
31
|
+
const contextSize = 16;
|
32
|
+
const start = Math.max(0, (position ?? 0) - contextSize);
|
33
|
+
const end = Math.min(data.length, (position ?? 0) + contextSize);
|
34
|
+
const chunk = data.slice(start, end);
|
35
|
+
const lines = ["Context:"];
|
36
|
+
let offset = start;
|
37
|
+
for (let i = 0; i < chunk.length; i += 16) {
|
38
|
+
const lineBytes = chunk.slice(i, i + 16);
|
39
|
+
const hex = Array.from(lineBytes).map((b) => b.toString(16).padStart(2, "0")).join(" ");
|
40
|
+
const ascii = Array.from(lineBytes).map((b) => b >= 32 && b <= 126 ? String.fromCharCode(b) : ".").join("");
|
41
|
+
const offsetStr = ` ${(offset + i).toString(16).padStart(8, "0")}`;
|
42
|
+
const marker = position !== void 0 && position >= offset + i && position < offset + i + lineBytes.length ? " <--" : "";
|
43
|
+
lines.push(`${offsetStr}: ${hex.padEnd(48, " ")} | ${ascii}${marker}`);
|
44
|
+
}
|
45
|
+
return lines.join("\n");
|
46
|
+
}
|
9
47
|
};
|
10
48
|
var ValidationError = class _ValidationError extends KaitaiError {
|
11
|
-
constructor(message, position) {
|
12
|
-
super(message, position);
|
49
|
+
constructor(message, position, context) {
|
50
|
+
super(message, position, context);
|
13
51
|
this.name = "ValidationError";
|
14
52
|
Object.setPrototypeOf(this, _ValidationError.prototype);
|
15
53
|
}
|
16
54
|
};
|
17
55
|
var ParseError = class _ParseError extends KaitaiError {
|
18
|
-
constructor(message, position) {
|
19
|
-
super(message, position);
|
56
|
+
constructor(message, position, context) {
|
57
|
+
super(message, position, context);
|
20
58
|
this.name = "ParseError";
|
21
59
|
Object.setPrototypeOf(this, _ParseError.prototype);
|
22
60
|
}
|
23
61
|
};
|
24
62
|
var EOFError = class _EOFError extends KaitaiError {
|
25
|
-
constructor(message = "Unexpected end of stream", position) {
|
26
|
-
super(message, position);
|
63
|
+
constructor(message = "Unexpected end of stream", position, context) {
|
64
|
+
super(message, position, context);
|
27
65
|
this.name = "EOFError";
|
28
66
|
Object.setPrototypeOf(this, _EOFError.prototype);
|
29
67
|
}
|
@@ -549,7 +587,9 @@ var BUILTIN_TYPES = [
|
|
549
587
|
"strz"
|
550
588
|
];
|
551
589
|
function isBuiltinType(type) {
|
552
|
-
|
590
|
+
if (BUILTIN_TYPES.includes(type)) return true;
|
591
|
+
if (/^b\d+$/.test(type)) return true;
|
592
|
+
return false;
|
553
593
|
}
|
554
594
|
function getTypeEndianness(type) {
|
555
595
|
if (type.endsWith("le")) return "le";
|
@@ -809,11 +849,83 @@ var KsyParser = class {
|
|
809
849
|
* @param imports - Map of import names to their YAML content
|
810
850
|
* @param options - Parsing options
|
811
851
|
* @returns Parsed schema with resolved imports
|
812
|
-
|
813
|
-
|
852
|
+
* @throws {ParseError} If import resolution fails
|
853
|
+
* @example
|
854
|
+
* ```typescript
|
855
|
+
* const parser = new KsyParser()
|
856
|
+
* const imports = new Map([
|
857
|
+
* ['/common/riff', riffYamlContent]
|
858
|
+
* ])
|
859
|
+
* const schema = parser.parseWithImports(wavYaml, imports)
|
860
|
+
* ```
|
861
|
+
*/
|
862
|
+
parseWithImports(mainYaml, imports, options = {}) {
|
814
863
|
const mainSchema = this.parse(mainYaml, options);
|
864
|
+
if (!mainSchema.meta.imports || mainSchema.meta.imports.length === 0) {
|
865
|
+
return mainSchema;
|
866
|
+
}
|
867
|
+
const resolvedTypes = {};
|
868
|
+
for (const importPath of mainSchema.meta.imports) {
|
869
|
+
if (!imports.has(importPath)) {
|
870
|
+
throw new ParseError(
|
871
|
+
`Import not found: ${importPath}. Available imports: ${Array.from(imports.keys()).join(", ")}`
|
872
|
+
);
|
873
|
+
}
|
874
|
+
const importYaml = imports.get(importPath);
|
875
|
+
const importedSchema = this.parse(importYaml, {
|
876
|
+
...options,
|
877
|
+
validate: false
|
878
|
+
// Skip validation for imported schemas
|
879
|
+
});
|
880
|
+
const namespace = this.extractNamespace(importPath);
|
881
|
+
if (importedSchema.types) {
|
882
|
+
for (const [typeName, typeSchema] of Object.entries(
|
883
|
+
importedSchema.types
|
884
|
+
)) {
|
885
|
+
const qualifiedName = `${namespace}::${typeName}`;
|
886
|
+
resolvedTypes[qualifiedName] = typeSchema;
|
887
|
+
}
|
888
|
+
}
|
889
|
+
resolvedTypes[namespace] = {
|
890
|
+
meta: importedSchema.meta,
|
891
|
+
seq: importedSchema.seq,
|
892
|
+
instances: importedSchema.instances,
|
893
|
+
types: importedSchema.types,
|
894
|
+
enums: importedSchema.enums
|
895
|
+
};
|
896
|
+
if (importedSchema.enums) {
|
897
|
+
if (!mainSchema.enums) {
|
898
|
+
mainSchema.enums = {};
|
899
|
+
}
|
900
|
+
for (const [enumName, enumSpec] of Object.entries(
|
901
|
+
importedSchema.enums
|
902
|
+
)) {
|
903
|
+
const qualifiedEnumName = `${namespace}::${enumName}`;
|
904
|
+
mainSchema.enums[qualifiedEnumName] = enumSpec;
|
905
|
+
}
|
906
|
+
}
|
907
|
+
}
|
908
|
+
if (Object.keys(resolvedTypes).length > 0) {
|
909
|
+
mainSchema.types = {
|
910
|
+
...resolvedTypes,
|
911
|
+
...mainSchema.types
|
912
|
+
};
|
913
|
+
}
|
815
914
|
return mainSchema;
|
816
915
|
}
|
916
|
+
/**
|
917
|
+
* Extract namespace from import path.
|
918
|
+
* Converts paths like '/common/riff' or 'common/riff' to 'riff'.
|
919
|
+
*
|
920
|
+
* @param importPath - Import path from meta.imports
|
921
|
+
* @returns Namespace identifier
|
922
|
+
* @private
|
923
|
+
*/
|
924
|
+
extractNamespace(importPath) {
|
925
|
+
const normalized = importPath.startsWith("/") ? importPath.slice(1) : importPath;
|
926
|
+
const segments = normalized.split("/");
|
927
|
+
return segments[segments.length - 1];
|
928
|
+
}
|
817
929
|
};
|
818
930
|
|
819
931
|
// src/interpreter/Context.ts
|
@@ -1124,6 +1236,17 @@ var Lexer = class {
|
|
1124
1236
|
}
|
1125
1237
|
return createToken("NUMBER" /* NUMBER */, parseInt(value, 16), start);
|
1126
1238
|
}
|
1239
|
+
if (this.current === "0" && this.peek() === "b") {
|
1240
|
+
value += this.current;
|
1241
|
+
this.advance();
|
1242
|
+
value += this.current;
|
1243
|
+
this.advance();
|
1244
|
+
while (this.current !== null && /[01]/.test(this.current)) {
|
1245
|
+
value += this.current;
|
1246
|
+
this.advance();
|
1247
|
+
}
|
1248
|
+
return createToken("NUMBER" /* NUMBER */, parseInt(value, 2), start);
|
1249
|
+
}
|
1127
1250
|
while (this.current !== null && this.isDigit(this.current)) {
|
1128
1251
|
value += this.current;
|
1129
1252
|
this.advance();
|
@@ -1335,6 +1458,9 @@ function createMethodCall(object, method, args) {
|
|
1335
1458
|
function createEnumAccess(enumName, value) {
|
1336
1459
|
return { kind: "EnumAccess", enumName, value };
|
1337
1460
|
}
|
1461
|
+
function createArrayLiteral(elements) {
|
1462
|
+
return { kind: "ArrayLiteral", elements };
|
1463
|
+
}
|
1338
1464
|
|
1339
1465
|
// src/expression/Parser.ts
|
1340
1466
|
var ExpressionParser = class {
|
@@ -1631,6 +1757,17 @@ var ExpressionParser = class {
|
|
1631
1757
|
this.expect("RPAREN" /* RPAREN */, "Expected ) after expression");
|
1632
1758
|
return expr;
|
1633
1759
|
}
|
1760
|
+
if (this.match("LBRACKET" /* LBRACKET */)) {
|
1761
|
+
const elements = [];
|
1762
|
+
if (this.current().type !== "RBRACKET" /* RBRACKET */) {
|
1763
|
+
elements.push(this.parseTernary());
|
1764
|
+
while (this.match("COMMA" /* COMMA */)) {
|
1765
|
+
elements.push(this.parseTernary());
|
1766
|
+
}
|
1767
|
+
}
|
1768
|
+
this.expect("RBRACKET" /* RBRACKET */, "Expected ] after array literal");
|
1769
|
+
return createArrayLiteral(elements);
|
1770
|
+
}
|
1634
1771
|
throw new ParseError(
|
1635
1772
|
`Unexpected token: ${this.current().type}`,
|
1636
1773
|
this.current().position
|
@@ -1669,6 +1806,8 @@ var Evaluator = class {
|
|
1669
1806
|
return this.evaluateMethodCall(n.object, n.method, n.args, context);
|
1670
1807
|
case "EnumAccess":
|
1671
1808
|
return this.evaluateEnumAccess(n.enumName, n.value, context);
|
1809
|
+
case "ArrayLiteral":
|
1810
|
+
return this.evaluateArrayLiteral(n.elements, context);
|
1672
1811
|
default:
|
1673
1812
|
throw new ParseError(`Unknown AST node kind: ${node.kind}`);
|
1674
1813
|
}
|
@@ -1714,15 +1853,15 @@ var Evaluator = class {
|
|
1714
1853
|
return !this.equals(leftVal, rightVal);
|
1715
1854
|
// Bitwise
|
1716
1855
|
case "<<":
|
1717
|
-
return this.
|
1856
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a << b);
|
1718
1857
|
case ">>":
|
1719
|
-
return this.
|
1858
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a >> b);
|
1720
1859
|
case "&":
|
1721
|
-
return this.
|
1860
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a & b);
|
1722
1861
|
case "|":
|
1723
|
-
return this.
|
1862
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a | b);
|
1724
1863
|
case "^":
|
1725
|
-
return this.
|
1864
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a ^ b);
|
1726
1865
|
// Logical
|
1727
1866
|
case "and":
|
1728
1867
|
return this.toBoolean(leftVal) && this.toBoolean(rightVal);
|
@@ -1766,6 +1905,16 @@ var Evaluator = class {
|
|
1766
1905
|
`Cannot access property ${property} of null/undefined`
|
1767
1906
|
);
|
1768
1907
|
}
|
1908
|
+
if (property === "to_i") {
|
1909
|
+
if (typeof obj === "number") return Math.floor(obj);
|
1910
|
+
if (typeof obj === "bigint") return Number(obj);
|
1911
|
+
if (typeof obj === "string") return parseInt(obj, 10);
|
1912
|
+
if (typeof obj === "boolean") return obj ? 1 : 0;
|
1913
|
+
return this.toInt(obj);
|
1914
|
+
}
|
1915
|
+
if (property === "to_s") {
|
1916
|
+
return String(obj);
|
1917
|
+
}
|
1769
1918
|
if (typeof obj === "object") {
|
1770
1919
|
return obj[property];
|
1771
1920
|
}
|
@@ -1792,8 +1941,9 @@ var Evaluator = class {
|
|
1792
1941
|
* Evaluate method call (object.method()).
|
1793
1942
|
* @private
|
1794
1943
|
*/
|
1795
|
-
evaluateMethodCall(object, method,
|
1944
|
+
evaluateMethodCall(object, method, args, context) {
|
1796
1945
|
const obj = this.evaluate(object, context);
|
1946
|
+
const evalArgs = args.map((arg) => this.evaluate(arg, context));
|
1797
1947
|
if (method === "length" || method === "size") {
|
1798
1948
|
if (Array.isArray(obj)) return obj.length;
|
1799
1949
|
if (obj instanceof Uint8Array) return obj.length;
|
@@ -1801,13 +1951,182 @@ var Evaluator = class {
|
|
1801
1951
|
throw new ParseError(`Object does not have a ${method} property`);
|
1802
1952
|
}
|
1803
1953
|
if (method === "to_i") {
|
1954
|
+
const base = evalArgs.length > 0 ? this.toInt(evalArgs[0]) : 10;
|
1955
|
+
if (typeof obj === "string") {
|
1956
|
+
return parseInt(obj, base);
|
1957
|
+
}
|
1804
1958
|
return this.toInt(obj);
|
1805
1959
|
}
|
1806
1960
|
if (method === "to_s") {
|
1807
1961
|
return String(obj);
|
1808
1962
|
}
|
1963
|
+
if (typeof obj === "string") {
|
1964
|
+
return this.evaluateStringMethod(obj, method, evalArgs);
|
1965
|
+
}
|
1966
|
+
if (Array.isArray(obj) || obj instanceof Uint8Array) {
|
1967
|
+
return this.evaluateArrayMethod(obj, method, evalArgs);
|
1968
|
+
}
|
1809
1969
|
throw new ParseError(`Unknown method: ${method}`);
|
1810
1970
|
}
|
1971
|
+
/**
|
1972
|
+
* Evaluate string methods.
|
1973
|
+
* @private
|
1974
|
+
*/
|
1975
|
+
evaluateStringMethod(str, method, args) {
|
1976
|
+
switch (method) {
|
1977
|
+
case "substring": {
|
1978
|
+
const start = args.length > 0 ? this.toInt(args[0]) : 0;
|
1979
|
+
const end = args.length > 1 ? this.toInt(args[1]) : void 0;
|
1980
|
+
return str.substring(start, end);
|
1981
|
+
}
|
1982
|
+
case "substr": {
|
1983
|
+
const start = args.length > 0 ? this.toInt(args[0]) : 0;
|
1984
|
+
const length = args.length > 1 ? this.toInt(args[1]) : void 0;
|
1985
|
+
return str.substr(start, length);
|
1986
|
+
}
|
1987
|
+
case "reverse":
|
1988
|
+
return str.split("").reverse().join("");
|
1989
|
+
case "to_i": {
|
1990
|
+
const base = args.length > 0 ? this.toInt(args[0]) : 10;
|
1991
|
+
return parseInt(str, base);
|
1992
|
+
}
|
1993
|
+
case "length":
|
1994
|
+
case "size":
|
1995
|
+
return str.length;
|
1996
|
+
// Ruby-style string methods used in Kaitai
|
1997
|
+
case "upcase":
|
1998
|
+
case "to_upper":
|
1999
|
+
return str.toUpperCase();
|
2000
|
+
case "downcase":
|
2001
|
+
case "to_lower":
|
2002
|
+
return str.toLowerCase();
|
2003
|
+
case "capitalize":
|
2004
|
+
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
2005
|
+
case "strip":
|
2006
|
+
case "trim":
|
2007
|
+
return str.trim();
|
2008
|
+
case "lstrip":
|
2009
|
+
case "trim_start":
|
2010
|
+
return str.trimStart();
|
2011
|
+
case "rstrip":
|
2012
|
+
case "trim_end":
|
2013
|
+
return str.trimEnd();
|
2014
|
+
case "starts_with":
|
2015
|
+
case "startsWith": {
|
2016
|
+
if (args.length === 0) {
|
2017
|
+
throw new ParseError("starts_with requires 1 argument");
|
2018
|
+
}
|
2019
|
+
return str.startsWith(String(args[0]));
|
2020
|
+
}
|
2021
|
+
case "ends_with":
|
2022
|
+
case "endsWith": {
|
2023
|
+
if (args.length === 0) {
|
2024
|
+
throw new ParseError("ends_with requires 1 argument");
|
2025
|
+
}
|
2026
|
+
return str.endsWith(String(args[0]));
|
2027
|
+
}
|
2028
|
+
case "includes":
|
2029
|
+
case "contains": {
|
2030
|
+
if (args.length === 0) {
|
2031
|
+
throw new ParseError("includes requires 1 argument");
|
2032
|
+
}
|
2033
|
+
return str.includes(String(args[0]));
|
2034
|
+
}
|
2035
|
+
case "index_of":
|
2036
|
+
case "indexOf": {
|
2037
|
+
if (args.length === 0) {
|
2038
|
+
throw new ParseError("index_of requires 1 argument");
|
2039
|
+
}
|
2040
|
+
return str.indexOf(String(args[0]));
|
2041
|
+
}
|
2042
|
+
case "split": {
|
2043
|
+
if (args.length === 0) {
|
2044
|
+
throw new ParseError("split requires 1 argument");
|
2045
|
+
}
|
2046
|
+
return str.split(String(args[0]));
|
2047
|
+
}
|
2048
|
+
case "replace": {
|
2049
|
+
if (args.length < 2) {
|
2050
|
+
throw new ParseError("replace requires 2 arguments");
|
2051
|
+
}
|
2052
|
+
return str.replace(String(args[0]), String(args[1]));
|
2053
|
+
}
|
2054
|
+
case "replace_all":
|
2055
|
+
case "replaceAll": {
|
2056
|
+
if (args.length < 2) {
|
2057
|
+
throw new ParseError("replace_all requires 2 arguments");
|
2058
|
+
}
|
2059
|
+
const search = String(args[0]);
|
2060
|
+
const replace = String(args[1]);
|
2061
|
+
return str.split(search).join(replace);
|
2062
|
+
}
|
2063
|
+
case "pad_left":
|
2064
|
+
case "padStart": {
|
2065
|
+
if (args.length === 0) {
|
2066
|
+
throw new ParseError("pad_left requires at least 1 argument");
|
2067
|
+
}
|
2068
|
+
const length = this.toInt(args[0]);
|
2069
|
+
const fillString = args.length > 1 ? String(args[1]) : " ";
|
2070
|
+
return str.padStart(length, fillString);
|
2071
|
+
}
|
2072
|
+
case "pad_right":
|
2073
|
+
case "padEnd": {
|
2074
|
+
if (args.length === 0) {
|
2075
|
+
throw new ParseError("pad_right requires at least 1 argument");
|
2076
|
+
}
|
2077
|
+
const length = this.toInt(args[0]);
|
2078
|
+
const fillString = args.length > 1 ? String(args[1]) : " ";
|
2079
|
+
return str.padEnd(length, fillString);
|
2080
|
+
}
|
2081
|
+
default:
|
2082
|
+
throw new ParseError(`Unknown string method: ${method}`);
|
2083
|
+
}
|
2084
|
+
}
|
2085
|
+
/**
|
2086
|
+
* Evaluate array methods.
|
2087
|
+
* @private
|
2088
|
+
*/
|
2089
|
+
evaluateArrayMethod(arr, method, args) {
|
2090
|
+
const array = Array.isArray(arr) ? arr : Array.from(arr);
|
2091
|
+
switch (method) {
|
2092
|
+
case "length":
|
2093
|
+
case "size":
|
2094
|
+
return array.length;
|
2095
|
+
case "first":
|
2096
|
+
return array[0];
|
2097
|
+
case "last":
|
2098
|
+
return array[array.length - 1];
|
2099
|
+
case "min":
|
2100
|
+
return Math.min(...array.map((v) => this.toNumber(v)));
|
2101
|
+
case "max":
|
2102
|
+
return Math.max(...array.map((v) => this.toNumber(v)));
|
2103
|
+
case "reverse":
|
2104
|
+
return [...array].reverse();
|
2105
|
+
case "sort":
|
2106
|
+
return [...array].sort((a, b) => this.compare(a, b));
|
2107
|
+
case "includes":
|
2108
|
+
case "contains": {
|
2109
|
+
if (args.length === 0) {
|
2110
|
+
throw new ParseError("includes requires 1 argument");
|
2111
|
+
}
|
2112
|
+
return array.some((item) => this.equals(item, args[0]));
|
2113
|
+
}
|
2114
|
+
case "index_of":
|
2115
|
+
case "indexOf": {
|
2116
|
+
if (args.length === 0) {
|
2117
|
+
throw new ParseError("index_of requires 1 argument");
|
2118
|
+
}
|
2119
|
+
return array.findIndex((item) => this.equals(item, args[0]));
|
2120
|
+
}
|
2121
|
+
case "slice": {
|
2122
|
+
const start = args.length > 0 ? this.toInt(args[0]) : 0;
|
2123
|
+
const end = args.length > 1 ? this.toInt(args[1]) : void 0;
|
2124
|
+
return array.slice(start, end);
|
2125
|
+
}
|
2126
|
+
default:
|
2127
|
+
throw new ParseError(`Unknown array method: ${method}`);
|
2128
|
+
}
|
2129
|
+
}
|
1811
2130
|
/**
|
1812
2131
|
* Evaluate enum access (EnumName::value).
|
1813
2132
|
* @private
|
@@ -1837,6 +2156,38 @@ var Evaluator = class {
|
|
1837
2156
|
const result = a % b;
|
1838
2157
|
return result < 0 ? result + b : result;
|
1839
2158
|
}
|
2159
|
+
/**
|
2160
|
+
* Helper: Bitwise operation with BigInt support.
|
2161
|
+
* JavaScript bitwise operators work on 32-bit integers, but Kaitai
|
2162
|
+
* may use 64-bit values. For values that fit in 32 bits, use native ops.
|
2163
|
+
* For larger values, convert to BigInt (with limitations).
|
2164
|
+
* @private
|
2165
|
+
*/
|
2166
|
+
bitwiseOp(left, right, op) {
|
2167
|
+
if (typeof left === "bigint" || typeof right === "bigint") {
|
2168
|
+
const leftBig = typeof left === "bigint" ? left : BigInt(left);
|
2169
|
+
const rightBig = typeof right === "bigint" ? right : BigInt(right);
|
2170
|
+
if (op.toString().includes("<<")) {
|
2171
|
+
return leftBig << BigInt(Number(rightBig));
|
2172
|
+
}
|
2173
|
+
if (op.toString().includes(">>")) {
|
2174
|
+
return leftBig >> BigInt(Number(rightBig));
|
2175
|
+
}
|
2176
|
+
if (op.toString().includes("&")) {
|
2177
|
+
return leftBig & rightBig;
|
2178
|
+
}
|
2179
|
+
if (op.toString().includes("|")) {
|
2180
|
+
return leftBig | rightBig;
|
2181
|
+
}
|
2182
|
+
if (op.toString().includes("^")) {
|
2183
|
+
return leftBig ^ rightBig;
|
2184
|
+
}
|
2185
|
+
}
|
2186
|
+
if (left === void 0 || left === null || right === void 0 || right === null) {
|
2187
|
+
throw new ParseError("Cannot perform bitwise operation on null/undefined");
|
2188
|
+
}
|
2189
|
+
return op(this.toInt(left), this.toInt(right));
|
2190
|
+
}
|
1840
2191
|
/**
|
1841
2192
|
* Helper: Compare two values.
|
1842
2193
|
* @private
|
@@ -1857,8 +2208,30 @@ var Evaluator = class {
|
|
1857
2208
|
if (typeof left === "bigint" || typeof right === "bigint") {
|
1858
2209
|
return BigInt(left) === BigInt(right);
|
1859
2210
|
}
|
2211
|
+
const toArray = (v) => {
|
2212
|
+
if (Array.isArray(v))
|
2213
|
+
return v.map((x) => typeof x === "bigint" ? Number(x) : x);
|
2214
|
+
if (v instanceof Uint8Array) return Array.from(v);
|
2215
|
+
return null;
|
2216
|
+
};
|
2217
|
+
const leftArr = toArray(left);
|
2218
|
+
const rightArr = toArray(right);
|
2219
|
+
if (leftArr && rightArr) {
|
2220
|
+
if (leftArr.length !== rightArr.length) return false;
|
2221
|
+
for (let i = 0; i < leftArr.length; i++) {
|
2222
|
+
if (leftArr[i] !== rightArr[i]) return false;
|
2223
|
+
}
|
2224
|
+
return true;
|
2225
|
+
}
|
1860
2226
|
return left === right;
|
1861
2227
|
}
|
2228
|
+
/**
|
2229
|
+
* Evaluate an array literal.
|
2230
|
+
* @private
|
2231
|
+
*/
|
2232
|
+
evaluateArrayLiteral(elements, context) {
|
2233
|
+
return elements.map((e) => this.evaluate(e, context));
|
2234
|
+
}
|
1862
2235
|
/**
|
1863
2236
|
* Helper: Convert to number.
|
1864
2237
|
* @private
|
@@ -1893,7 +2266,8 @@ var Evaluator = class {
|
|
1893
2266
|
|
1894
2267
|
// src/expression/index.ts
|
1895
2268
|
function evaluateExpression(expression, context) {
|
1896
|
-
const
|
2269
|
+
const preprocessed = expression.replace(/\.as<[^>]+>/g, "");
|
2270
|
+
const lexer = new Lexer(preprocessed);
|
1897
2271
|
const tokens = lexer.tokenize();
|
1898
2272
|
const parser = new ExpressionParser(tokens);
|
1899
2273
|
const ast = parser.parse();
|
@@ -1901,6 +2275,112 @@ function evaluateExpression(expression, context) {
|
|
1901
2275
|
return evaluator.evaluate(ast, context);
|
1902
2276
|
}
|
1903
2277
|
|
2278
|
+
// src/utils/process.ts
|
2279
|
+
import { inflate } from "pako";
|
2280
|
+
function applyProcess(data, process) {
|
2281
|
+
const spec = typeof process === "string" ? { algorithm: process } : process;
|
2282
|
+
const algorithm = spec.algorithm;
|
2283
|
+
if (!algorithm) {
|
2284
|
+
throw new ParseError("Process specification missing algorithm");
|
2285
|
+
}
|
2286
|
+
switch (algorithm) {
|
2287
|
+
case "zlib":
|
2288
|
+
return processZlib(data);
|
2289
|
+
case "xor":
|
2290
|
+
return processXor(data, spec.key);
|
2291
|
+
case "rol":
|
2292
|
+
return processRol(data, spec.amount, spec.group);
|
2293
|
+
case "ror":
|
2294
|
+
return processRor(data, spec.amount, spec.group);
|
2295
|
+
case "bswap2":
|
2296
|
+
return processByteswap(data, 2);
|
2297
|
+
case "bswap4":
|
2298
|
+
return processByteswap(data, 4);
|
2299
|
+
case "bswap8":
|
2300
|
+
return processByteswap(data, 8);
|
2301
|
+
case "bswap16":
|
2302
|
+
return processByteswap(data, 16);
|
2303
|
+
default:
|
2304
|
+
throw new ParseError(
|
2305
|
+
`Unknown process algorithm: ${algorithm}. Supported: zlib, xor, rol, ror, bswap2, bswap4, bswap8, bswap16`
|
2306
|
+
);
|
2307
|
+
}
|
2308
|
+
}
|
2309
|
+
function processZlib(data) {
|
2310
|
+
try {
|
2311
|
+
return inflate(data);
|
2312
|
+
} catch (error) {
|
2313
|
+
throw new ParseError(
|
2314
|
+
`Zlib decompression failed: ${error instanceof Error ? error.message : String(error)}`
|
2315
|
+
);
|
2316
|
+
}
|
2317
|
+
}
|
2318
|
+
function processXor(data, key) {
|
2319
|
+
if (key === void 0) {
|
2320
|
+
throw new ParseError("XOR process requires a key parameter");
|
2321
|
+
}
|
2322
|
+
const result = new Uint8Array(data.length);
|
2323
|
+
const keyBytes = Array.isArray(key) ? key : [key];
|
2324
|
+
if (keyBytes.length === 0) {
|
2325
|
+
throw new ParseError("XOR key cannot be empty");
|
2326
|
+
}
|
2327
|
+
for (let i = 0; i < data.length; i++) {
|
2328
|
+
result[i] = data[i] ^ keyBytes[i % keyBytes.length];
|
2329
|
+
}
|
2330
|
+
return result;
|
2331
|
+
}
|
2332
|
+
function processRol(data, amount, group) {
|
2333
|
+
const bits = amount ?? 1;
|
2334
|
+
const groupSize = group ?? 1;
|
2335
|
+
if (bits < 0 || bits > 7) {
|
2336
|
+
throw new ParseError("ROL amount must be between 0 and 7");
|
2337
|
+
}
|
2338
|
+
if (groupSize !== 1) {
|
2339
|
+
throw new ParseError("ROL with group size > 1 not yet supported");
|
2340
|
+
}
|
2341
|
+
const result = new Uint8Array(data.length);
|
2342
|
+
for (let i = 0; i < data.length; i++) {
|
2343
|
+
const byte = data[i];
|
2344
|
+
result[i] = (byte << bits | byte >> 8 - bits) & 255;
|
2345
|
+
}
|
2346
|
+
return result;
|
2347
|
+
}
|
2348
|
+
function processRor(data, amount, group) {
|
2349
|
+
const bits = amount ?? 1;
|
2350
|
+
const groupSize = group ?? 1;
|
2351
|
+
if (bits < 0 || bits > 7) {
|
2352
|
+
throw new ParseError("ROR amount must be between 0 and 7");
|
2353
|
+
}
|
2354
|
+
if (groupSize !== 1) {
|
2355
|
+
throw new ParseError("ROR with group size > 1 not yet supported");
|
2356
|
+
}
|
2357
|
+
const result = new Uint8Array(data.length);
|
2358
|
+
for (let i = 0; i < data.length; i++) {
|
2359
|
+
const byte = data[i];
|
2360
|
+
result[i] = (byte >> bits | byte << 8 - bits) & 255;
|
2361
|
+
}
|
2362
|
+
return result;
|
2363
|
+
}
|
2364
|
+
function processByteswap(data, groupSize) {
|
2365
|
+
if (![2, 4, 8, 16].includes(groupSize)) {
|
2366
|
+
throw new ParseError(
|
2367
|
+
`Invalid byteswap group size: ${groupSize}. Must be 2, 4, 8, or 16`
|
2368
|
+
);
|
2369
|
+
}
|
2370
|
+
if (data.length % groupSize !== 0) {
|
2371
|
+
throw new ParseError(
|
2372
|
+
`Data length ${data.length} is not aligned to group size ${groupSize}`
|
2373
|
+
);
|
2374
|
+
}
|
2375
|
+
const result = new Uint8Array(data.length);
|
2376
|
+
for (let i = 0; i < data.length; i += groupSize) {
|
2377
|
+
for (let j = 0; j < groupSize; j++) {
|
2378
|
+
result[i + j] = data[i + groupSize - 1 - j];
|
2379
|
+
}
|
2380
|
+
}
|
2381
|
+
return result;
|
2382
|
+
}
|
2383
|
+
|
1904
2384
|
// src/interpreter/TypeInterpreter.ts
|
1905
2385
|
var TypeInterpreter = class _TypeInterpreter {
|
1906
2386
|
/**
|
@@ -1919,18 +2399,38 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
1919
2399
|
throw new ParseError("Root schema must have meta.id");
|
1920
2400
|
}
|
1921
2401
|
}
|
2402
|
+
/**
|
2403
|
+
* Safely extract a KaitaiStream from an object that may expose `_io`.
|
2404
|
+
* Avoids using `any` casts to satisfy linting.
|
2405
|
+
*/
|
2406
|
+
static getKaitaiIO(val) {
|
2407
|
+
if (val && typeof val === "object") {
|
2408
|
+
const rec = val;
|
2409
|
+
const maybe = rec["_io"];
|
2410
|
+
if (maybe instanceof KaitaiStream) return maybe;
|
2411
|
+
}
|
2412
|
+
return null;
|
2413
|
+
}
|
1922
2414
|
/**
|
1923
2415
|
* Parse binary data according to the schema.
|
1924
2416
|
*
|
1925
2417
|
* @param stream - Binary stream to parse
|
1926
2418
|
* @param parent - Parent object (for nested types)
|
1927
2419
|
* @param typeArgs - Arguments for parametric types
|
2420
|
+
* @param root - Root object of the parse tree (for nested types)
|
1928
2421
|
* @returns Parsed object
|
1929
2422
|
*/
|
1930
|
-
parse(stream, parent, typeArgs) {
|
2423
|
+
parse(stream, parent, typeArgs, root) {
|
1931
2424
|
const result = {};
|
1932
|
-
const
|
2425
|
+
const actualRoot = root || result;
|
2426
|
+
const context = new Context(stream, actualRoot, parent, this.schema.enums);
|
1933
2427
|
context.current = result;
|
2428
|
+
const startPos = stream.pos;
|
2429
|
+
result["_io"] = stream;
|
2430
|
+
if (root) {
|
2431
|
+
;
|
2432
|
+
result["_root"] = root;
|
2433
|
+
}
|
1934
2434
|
if (typeArgs && this.schema.params) {
|
1935
2435
|
for (let i = 0; i < this.schema.params.length && i < typeArgs.length; i++) {
|
1936
2436
|
const param = this.schema.params[i];
|
@@ -1939,6 +2439,9 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
1939
2439
|
context.set(param.id, evaluatedArg);
|
1940
2440
|
}
|
1941
2441
|
}
|
2442
|
+
if (this.schema.instances) {
|
2443
|
+
this.setupInstances(result, stream, context);
|
2444
|
+
}
|
1942
2445
|
if (this.schema.seq) {
|
1943
2446
|
for (const attr of this.schema.seq) {
|
1944
2447
|
const value = this.parseAttribute(attr, context);
|
@@ -1947,9 +2450,8 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
1947
2450
|
}
|
1948
2451
|
}
|
1949
2452
|
}
|
1950
|
-
|
1951
|
-
|
1952
|
-
}
|
2453
|
+
const endPos = stream.pos;
|
2454
|
+
result["_sizeof"] = endPos - startPos;
|
1953
2455
|
return result;
|
1954
2456
|
}
|
1955
2457
|
/**
|
@@ -2033,7 +2535,22 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2033
2535
|
* @private
|
2034
2536
|
*/
|
2035
2537
|
parseAttribute(attr, context) {
|
2036
|
-
|
2538
|
+
let stream = context.io;
|
2539
|
+
if (attr.io !== void 0) {
|
2540
|
+
const ioVal = this.evaluateValue(attr.io, context);
|
2541
|
+
if (ioVal instanceof KaitaiStream) {
|
2542
|
+
stream = ioVal;
|
2543
|
+
} else {
|
2544
|
+
const kio = _TypeInterpreter.getKaitaiIO(ioVal);
|
2545
|
+
if (kio) {
|
2546
|
+
stream = kio;
|
2547
|
+
} else {
|
2548
|
+
throw new ParseError(
|
2549
|
+
"io must evaluate to a KaitaiStream or an object with _io"
|
2550
|
+
);
|
2551
|
+
}
|
2552
|
+
}
|
2553
|
+
}
|
2037
2554
|
if (attr.if) {
|
2038
2555
|
const condition = this.evaluateValue(attr.if, context);
|
2039
2556
|
if (!condition) {
|
@@ -2050,9 +2567,6 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2050
2567
|
throw new ParseError(`pos must evaluate to a number, got ${typeof pos}`);
|
2051
2568
|
}
|
2052
2569
|
}
|
2053
|
-
if (attr.io) {
|
2054
|
-
throw new NotImplementedError("Custom I/O streams");
|
2055
|
-
}
|
2056
2570
|
if (attr.repeat) {
|
2057
2571
|
return this.parseRepeated(attr, context);
|
2058
2572
|
}
|
@@ -2149,7 +2663,7 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2149
2663
|
}
|
2150
2664
|
return bytes;
|
2151
2665
|
} else {
|
2152
|
-
const encoding = attr.encoding || this.schema.meta.encoding || "UTF-8";
|
2666
|
+
const encoding = attr.encoding || this.schema.meta?.encoding || this.parentMeta?.encoding || "UTF-8";
|
2153
2667
|
const str = stream.readStr(expected.length, encoding);
|
2154
2668
|
if (str !== expected) {
|
2155
2669
|
throw new ValidationError(
|
@@ -2178,7 +2692,7 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2178
2692
|
throw new ParseError(`size must be non-negative, got ${size}`);
|
2179
2693
|
}
|
2180
2694
|
if (type === "str" || !type) {
|
2181
|
-
const encoding = attr.encoding || this.schema.meta.encoding || "UTF-8";
|
2695
|
+
const encoding = attr.encoding || this.schema.meta?.encoding || this.parentMeta?.encoding || "UTF-8";
|
2182
2696
|
let data;
|
2183
2697
|
if (type === "str") {
|
2184
2698
|
data = stream.readBytes(size);
|
@@ -2204,11 +2718,19 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2204
2718
|
}
|
2205
2719
|
if (attr["size-eos"]) {
|
2206
2720
|
if (type === "str") {
|
2207
|
-
const encoding = attr.encoding || this.schema.meta.encoding || "UTF-8";
|
2721
|
+
const encoding = attr.encoding || this.schema.meta?.encoding || this.parentMeta?.encoding || "UTF-8";
|
2208
2722
|
const bytes = stream.readBytesFull();
|
2209
2723
|
return new TextDecoder(encoding).decode(bytes);
|
2210
2724
|
} else {
|
2211
|
-
|
2725
|
+
let bytes = stream.readBytesFull();
|
2726
|
+
if (attr.process) {
|
2727
|
+
bytes = this.applyProcessing(bytes, attr.process);
|
2728
|
+
}
|
2729
|
+
if (type) {
|
2730
|
+
const sub = new KaitaiStream(bytes);
|
2731
|
+
return this.parseType(type, sub, context, attr["type-args"]);
|
2732
|
+
}
|
2733
|
+
return bytes;
|
2212
2734
|
}
|
2213
2735
|
}
|
2214
2736
|
if (!type) {
|
@@ -2234,11 +2756,13 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2234
2756
|
context
|
2235
2757
|
);
|
2236
2758
|
}
|
2237
|
-
|
2238
|
-
|
2759
|
+
const { typeName, args } = this.parseParameterizedType(type, context);
|
2760
|
+
const effectiveArgs = args.length > 0 ? args : typeArgs;
|
2761
|
+
if (isBuiltinType(typeName)) {
|
2762
|
+
return this.parseBuiltinType(typeName, stream, context);
|
2239
2763
|
}
|
2240
|
-
if (this.schema.types &&
|
2241
|
-
const typeSchema = this.schema.types[
|
2764
|
+
if (this.schema.types && typeName in this.schema.types) {
|
2765
|
+
const typeSchema = this.schema.types[typeName];
|
2242
2766
|
const meta = this.schema.meta || this.parentMeta;
|
2243
2767
|
if (this.schema.enums && !typeSchema.enums) {
|
2244
2768
|
typeSchema.enums = this.schema.enums;
|
@@ -2247,9 +2771,100 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2247
2771
|
typeSchema.types = this.schema.types;
|
2248
2772
|
}
|
2249
2773
|
const interpreter = new _TypeInterpreter(typeSchema, meta);
|
2250
|
-
return interpreter.parse(
|
2774
|
+
return interpreter.parse(
|
2775
|
+
stream,
|
2776
|
+
context.current,
|
2777
|
+
effectiveArgs,
|
2778
|
+
context.root
|
2779
|
+
);
|
2780
|
+
}
|
2781
|
+
throw new ParseError(`Unknown type: ${typeName}`);
|
2782
|
+
}
|
2783
|
+
/**
|
2784
|
+
* Parse parameterized type syntax and extract type name and arguments.
|
2785
|
+
* Supports: type_name(arg1, arg2, ...) or just type_name
|
2786
|
+
*
|
2787
|
+
* @param typeSpec - Type specification string
|
2788
|
+
* @param context - Execution context for evaluating argument expressions
|
2789
|
+
* @returns Object with typeName and evaluated args
|
2790
|
+
* @private
|
2791
|
+
*/
|
2792
|
+
parseParameterizedType(typeSpec, context) {
|
2793
|
+
const match = typeSpec.match(/^([a-z_][a-z0-9_]*)\((.*)\)$/i);
|
2794
|
+
if (!match) {
|
2795
|
+
return { typeName: typeSpec, args: [] };
|
2796
|
+
}
|
2797
|
+
const typeName = match[1];
|
2798
|
+
const argsString = match[2].trim();
|
2799
|
+
if (!argsString) {
|
2800
|
+
return { typeName, args: [] };
|
2801
|
+
}
|
2802
|
+
const args = [];
|
2803
|
+
let current = "";
|
2804
|
+
let inString = false;
|
2805
|
+
let stringChar = "";
|
2806
|
+
let parenDepth = 0;
|
2807
|
+
for (let i = 0; i < argsString.length; i++) {
|
2808
|
+
const char = argsString[i];
|
2809
|
+
if (inString) {
|
2810
|
+
current += char;
|
2811
|
+
if (char === stringChar && argsString[i - 1] !== "\\") {
|
2812
|
+
inString = false;
|
2813
|
+
}
|
2814
|
+
} else if (char === '"' || char === "'") {
|
2815
|
+
inString = true;
|
2816
|
+
stringChar = char;
|
2817
|
+
current += char;
|
2818
|
+
} else if (char === "(") {
|
2819
|
+
parenDepth++;
|
2820
|
+
current += char;
|
2821
|
+
} else if (char === ")") {
|
2822
|
+
parenDepth--;
|
2823
|
+
current += char;
|
2824
|
+
} else if (char === "," && parenDepth === 0) {
|
2825
|
+
args.push(this.parseArgument(current.trim(), context));
|
2826
|
+
current = "";
|
2827
|
+
} else {
|
2828
|
+
current += char;
|
2829
|
+
}
|
2830
|
+
}
|
2831
|
+
if (current.trim()) {
|
2832
|
+
args.push(this.parseArgument(current.trim(), context));
|
2833
|
+
}
|
2834
|
+
return { typeName, args };
|
2835
|
+
}
|
2836
|
+
/**
|
2837
|
+
* Parse and evaluate a single type argument.
|
2838
|
+
*
|
2839
|
+
* @param arg - Argument string
|
2840
|
+
* @param context - Execution context
|
2841
|
+
* @returns Evaluated argument value
|
2842
|
+
* @private
|
2843
|
+
*/
|
2844
|
+
parseArgument(arg, context) {
|
2845
|
+
if (arg === "true") return true;
|
2846
|
+
if (arg === "false") return false;
|
2847
|
+
if (arg.startsWith('"') && arg.endsWith('"') || arg.startsWith("'") && arg.endsWith("'")) {
|
2848
|
+
return arg.slice(1, -1);
|
2849
|
+
}
|
2850
|
+
if (/^-?\d+$/.test(arg)) {
|
2851
|
+
return parseInt(arg, 10);
|
2852
|
+
}
|
2853
|
+
if (/^-?\d+\.\d+$/.test(arg)) {
|
2854
|
+
return parseFloat(arg);
|
2855
|
+
}
|
2856
|
+
if (/^0x[0-9a-f]+$/i.test(arg)) {
|
2857
|
+
return parseInt(arg, 16);
|
2858
|
+
}
|
2859
|
+
try {
|
2860
|
+
const result = this.evaluateValue(arg, context);
|
2861
|
+
if (typeof result === "string" || typeof result === "number" || typeof result === "boolean") {
|
2862
|
+
return result;
|
2863
|
+
}
|
2864
|
+
return Number(result);
|
2865
|
+
} catch {
|
2866
|
+
return arg;
|
2251
2867
|
}
|
2252
|
-
throw new ParseError(`Unknown type: ${type}`);
|
2253
2868
|
}
|
2254
2869
|
/**
|
2255
2870
|
* Parse a switch type (type selection based on expression).
|
@@ -2292,18 +2907,33 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2292
2907
|
* @returns Parsed value
|
2293
2908
|
* @private
|
2294
2909
|
*/
|
2295
|
-
parseBuiltinType(type, stream,
|
2910
|
+
parseBuiltinType(type, stream, context) {
|
2296
2911
|
const base = getBaseType(type);
|
2297
2912
|
const typeEndian = getTypeEndianness(type);
|
2298
2913
|
const meta = this.schema.meta || this.parentMeta;
|
2299
2914
|
const metaEndian = meta?.endian;
|
2300
|
-
|
2915
|
+
let endian;
|
2916
|
+
if (typeEndian) {
|
2917
|
+
endian = typeEndian;
|
2918
|
+
} else if (typeof metaEndian === "string") {
|
2919
|
+
endian = metaEndian;
|
2920
|
+
} else if (metaEndian && typeof metaEndian === "object") {
|
2921
|
+
endian = this.evaluateEndianExpression(metaEndian, context);
|
2922
|
+
} else {
|
2923
|
+
endian = "le";
|
2924
|
+
}
|
2301
2925
|
if (isIntegerType(type)) {
|
2302
2926
|
return this.readInteger(base, endian, stream);
|
2303
2927
|
}
|
2304
2928
|
if (isFloatType(type)) {
|
2305
2929
|
return this.readFloat(base, endian, stream);
|
2306
2930
|
}
|
2931
|
+
if (/^b\d+$/.test(type)) {
|
2932
|
+
const n = parseInt(type.slice(1), 10);
|
2933
|
+
const val = stream.readBitsIntBe(n);
|
2934
|
+
const maxSafe = BigInt(Number.MAX_SAFE_INTEGER);
|
2935
|
+
return val <= maxSafe ? Number(val) : val;
|
2936
|
+
}
|
2307
2937
|
if (isStringType(type)) {
|
2308
2938
|
const encoding = this.schema.meta?.encoding || "UTF-8";
|
2309
2939
|
if (type === "strz") {
|
@@ -2368,7 +2998,7 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2368
2998
|
}
|
2369
2999
|
/**
|
2370
3000
|
* Apply processing transformation to data.
|
2371
|
-
*
|
3001
|
+
* Delegates to the process utility module.
|
2372
3002
|
*
|
2373
3003
|
* @param data - Data to process
|
2374
3004
|
* @param process - Processing specification
|
@@ -2376,13 +3006,35 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2376
3006
|
* @private
|
2377
3007
|
*/
|
2378
3008
|
applyProcessing(data, process) {
|
2379
|
-
|
2380
|
-
|
2381
|
-
|
2382
|
-
|
2383
|
-
|
3009
|
+
return applyProcess(data, process);
|
3010
|
+
}
|
3011
|
+
/**
|
3012
|
+
* Evaluate expression-based endianness (switch-on).
|
3013
|
+
*
|
3014
|
+
* @param endianExpr - Endianness expression with switch-on and cases
|
3015
|
+
* @param context - Execution context
|
3016
|
+
* @returns Resolved endianness ('le' or 'be')
|
3017
|
+
* @private
|
3018
|
+
*/
|
3019
|
+
evaluateEndianExpression(endianExpr, context) {
|
3020
|
+
const switchOn = endianExpr["switch-on"];
|
3021
|
+
const cases = endianExpr.cases;
|
3022
|
+
if (!switchOn || typeof switchOn !== "string") {
|
3023
|
+
throw new ParseError('Endian expression missing "switch-on" field');
|
3024
|
+
}
|
3025
|
+
if (!cases || typeof cases !== "object") {
|
3026
|
+
throw new ParseError('Endian expression missing "cases" field');
|
3027
|
+
}
|
3028
|
+
const switchValue = this.evaluateValue(switchOn, context);
|
3029
|
+
const key = String(switchValue);
|
3030
|
+
if (key in cases) {
|
3031
|
+
const endian = cases[key];
|
3032
|
+
if (endian !== "le" && endian !== "be") {
|
3033
|
+
throw new ParseError(`Invalid endianness value: ${endian}`);
|
3034
|
+
}
|
3035
|
+
return endian;
|
2384
3036
|
}
|
2385
|
-
return
|
3037
|
+
return "le";
|
2386
3038
|
}
|
2387
3039
|
/**
|
2388
3040
|
* Evaluate a value that can be an expression or literal.
|
@@ -2524,6 +3176,12 @@ export {
|
|
2524
3176
|
* @author Fabiano Pinto
|
2525
3177
|
* @license MIT
|
2526
3178
|
*/
|
3179
|
+
/**
|
3180
|
+
* @fileoverview Data processing utilities for Kaitai Struct
|
3181
|
+
* @module utils/process
|
3182
|
+
* @author Fabiano Pinto
|
3183
|
+
* @license MIT
|
3184
|
+
*/
|
2527
3185
|
/**
|
2528
3186
|
* @fileoverview Type interpreter for executing Kaitai Struct schemas
|
2529
3187
|
* @module interpreter/TypeInterpreter
|