@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.js
CHANGED
@@ -42,30 +42,68 @@ module.exports = __toCommonJS(index_exports);
|
|
42
42
|
|
43
43
|
// src/utils/errors.ts
|
44
44
|
var KaitaiError = class _KaitaiError extends Error {
|
45
|
-
constructor(message, position) {
|
46
|
-
super(message);
|
45
|
+
constructor(message, position, context) {
|
46
|
+
super(_KaitaiError.formatMessage(message, position, context));
|
47
47
|
this.position = position;
|
48
|
+
this.context = context;
|
48
49
|
this.name = "KaitaiError";
|
49
50
|
Object.setPrototypeOf(this, _KaitaiError.prototype);
|
50
51
|
}
|
52
|
+
/**
|
53
|
+
* Format error message with position and context.
|
54
|
+
* @private
|
55
|
+
*/
|
56
|
+
static formatMessage(message, position, context) {
|
57
|
+
let formatted = message;
|
58
|
+
if (position !== void 0) {
|
59
|
+
formatted += ` (at byte offset 0x${position.toString(16).toUpperCase()})`;
|
60
|
+
}
|
61
|
+
if (context && context.length > 0) {
|
62
|
+
const hexContext = _KaitaiError.formatHexContext(context, position);
|
63
|
+
formatted += `
|
64
|
+
${hexContext}`;
|
65
|
+
}
|
66
|
+
return formatted;
|
67
|
+
}
|
68
|
+
/**
|
69
|
+
* Format hex dump context around error position.
|
70
|
+
* @private
|
71
|
+
*/
|
72
|
+
static formatHexContext(data, position) {
|
73
|
+
const contextSize = 16;
|
74
|
+
const start = Math.max(0, (position ?? 0) - contextSize);
|
75
|
+
const end = Math.min(data.length, (position ?? 0) + contextSize);
|
76
|
+
const chunk = data.slice(start, end);
|
77
|
+
const lines = ["Context:"];
|
78
|
+
let offset = start;
|
79
|
+
for (let i = 0; i < chunk.length; i += 16) {
|
80
|
+
const lineBytes = chunk.slice(i, i + 16);
|
81
|
+
const hex = Array.from(lineBytes).map((b) => b.toString(16).padStart(2, "0")).join(" ");
|
82
|
+
const ascii = Array.from(lineBytes).map((b) => b >= 32 && b <= 126 ? String.fromCharCode(b) : ".").join("");
|
83
|
+
const offsetStr = ` ${(offset + i).toString(16).padStart(8, "0")}`;
|
84
|
+
const marker = position !== void 0 && position >= offset + i && position < offset + i + lineBytes.length ? " <--" : "";
|
85
|
+
lines.push(`${offsetStr}: ${hex.padEnd(48, " ")} | ${ascii}${marker}`);
|
86
|
+
}
|
87
|
+
return lines.join("\n");
|
88
|
+
}
|
51
89
|
};
|
52
90
|
var ValidationError = class _ValidationError extends KaitaiError {
|
53
|
-
constructor(message, position) {
|
54
|
-
super(message, position);
|
91
|
+
constructor(message, position, context) {
|
92
|
+
super(message, position, context);
|
55
93
|
this.name = "ValidationError";
|
56
94
|
Object.setPrototypeOf(this, _ValidationError.prototype);
|
57
95
|
}
|
58
96
|
};
|
59
97
|
var ParseError = class _ParseError extends KaitaiError {
|
60
|
-
constructor(message, position) {
|
61
|
-
super(message, position);
|
98
|
+
constructor(message, position, context) {
|
99
|
+
super(message, position, context);
|
62
100
|
this.name = "ParseError";
|
63
101
|
Object.setPrototypeOf(this, _ParseError.prototype);
|
64
102
|
}
|
65
103
|
};
|
66
104
|
var EOFError = class _EOFError extends KaitaiError {
|
67
|
-
constructor(message = "Unexpected end of stream", position) {
|
68
|
-
super(message, position);
|
105
|
+
constructor(message = "Unexpected end of stream", position, context) {
|
106
|
+
super(message, position, context);
|
69
107
|
this.name = "EOFError";
|
70
108
|
Object.setPrototypeOf(this, _EOFError.prototype);
|
71
109
|
}
|
@@ -591,7 +629,9 @@ var BUILTIN_TYPES = [
|
|
591
629
|
"strz"
|
592
630
|
];
|
593
631
|
function isBuiltinType(type) {
|
594
|
-
|
632
|
+
if (BUILTIN_TYPES.includes(type)) return true;
|
633
|
+
if (/^b\d+$/.test(type)) return true;
|
634
|
+
return false;
|
595
635
|
}
|
596
636
|
function getTypeEndianness(type) {
|
597
637
|
if (type.endsWith("le")) return "le";
|
@@ -851,11 +891,83 @@ var KsyParser = class {
|
|
851
891
|
* @param imports - Map of import names to their YAML content
|
852
892
|
* @param options - Parsing options
|
853
893
|
* @returns Parsed schema with resolved imports
|
854
|
-
|
855
|
-
|
894
|
+
* @throws {ParseError} If import resolution fails
|
895
|
+
* @example
|
896
|
+
* ```typescript
|
897
|
+
* const parser = new KsyParser()
|
898
|
+
* const imports = new Map([
|
899
|
+
* ['/common/riff', riffYamlContent]
|
900
|
+
* ])
|
901
|
+
* const schema = parser.parseWithImports(wavYaml, imports)
|
902
|
+
* ```
|
903
|
+
*/
|
904
|
+
parseWithImports(mainYaml, imports, options = {}) {
|
856
905
|
const mainSchema = this.parse(mainYaml, options);
|
906
|
+
if (!mainSchema.meta.imports || mainSchema.meta.imports.length === 0) {
|
907
|
+
return mainSchema;
|
908
|
+
}
|
909
|
+
const resolvedTypes = {};
|
910
|
+
for (const importPath of mainSchema.meta.imports) {
|
911
|
+
if (!imports.has(importPath)) {
|
912
|
+
throw new ParseError(
|
913
|
+
`Import not found: ${importPath}. Available imports: ${Array.from(imports.keys()).join(", ")}`
|
914
|
+
);
|
915
|
+
}
|
916
|
+
const importYaml = imports.get(importPath);
|
917
|
+
const importedSchema = this.parse(importYaml, {
|
918
|
+
...options,
|
919
|
+
validate: false
|
920
|
+
// Skip validation for imported schemas
|
921
|
+
});
|
922
|
+
const namespace = this.extractNamespace(importPath);
|
923
|
+
if (importedSchema.types) {
|
924
|
+
for (const [typeName, typeSchema] of Object.entries(
|
925
|
+
importedSchema.types
|
926
|
+
)) {
|
927
|
+
const qualifiedName = `${namespace}::${typeName}`;
|
928
|
+
resolvedTypes[qualifiedName] = typeSchema;
|
929
|
+
}
|
930
|
+
}
|
931
|
+
resolvedTypes[namespace] = {
|
932
|
+
meta: importedSchema.meta,
|
933
|
+
seq: importedSchema.seq,
|
934
|
+
instances: importedSchema.instances,
|
935
|
+
types: importedSchema.types,
|
936
|
+
enums: importedSchema.enums
|
937
|
+
};
|
938
|
+
if (importedSchema.enums) {
|
939
|
+
if (!mainSchema.enums) {
|
940
|
+
mainSchema.enums = {};
|
941
|
+
}
|
942
|
+
for (const [enumName, enumSpec] of Object.entries(
|
943
|
+
importedSchema.enums
|
944
|
+
)) {
|
945
|
+
const qualifiedEnumName = `${namespace}::${enumName}`;
|
946
|
+
mainSchema.enums[qualifiedEnumName] = enumSpec;
|
947
|
+
}
|
948
|
+
}
|
949
|
+
}
|
950
|
+
if (Object.keys(resolvedTypes).length > 0) {
|
951
|
+
mainSchema.types = {
|
952
|
+
...resolvedTypes,
|
953
|
+
...mainSchema.types
|
954
|
+
};
|
955
|
+
}
|
857
956
|
return mainSchema;
|
858
957
|
}
|
958
|
+
/**
|
959
|
+
* Extract namespace from import path.
|
960
|
+
* Converts paths like '/common/riff' or 'common/riff' to 'riff'.
|
961
|
+
*
|
962
|
+
* @param importPath - Import path from meta.imports
|
963
|
+
* @returns Namespace identifier
|
964
|
+
* @private
|
965
|
+
*/
|
966
|
+
extractNamespace(importPath) {
|
967
|
+
const normalized = importPath.startsWith("/") ? importPath.slice(1) : importPath;
|
968
|
+
const segments = normalized.split("/");
|
969
|
+
return segments[segments.length - 1];
|
970
|
+
}
|
859
971
|
};
|
860
972
|
|
861
973
|
// src/interpreter/Context.ts
|
@@ -1166,6 +1278,17 @@ var Lexer = class {
|
|
1166
1278
|
}
|
1167
1279
|
return createToken("NUMBER" /* NUMBER */, parseInt(value, 16), start);
|
1168
1280
|
}
|
1281
|
+
if (this.current === "0" && this.peek() === "b") {
|
1282
|
+
value += this.current;
|
1283
|
+
this.advance();
|
1284
|
+
value += this.current;
|
1285
|
+
this.advance();
|
1286
|
+
while (this.current !== null && /[01]/.test(this.current)) {
|
1287
|
+
value += this.current;
|
1288
|
+
this.advance();
|
1289
|
+
}
|
1290
|
+
return createToken("NUMBER" /* NUMBER */, parseInt(value, 2), start);
|
1291
|
+
}
|
1169
1292
|
while (this.current !== null && this.isDigit(this.current)) {
|
1170
1293
|
value += this.current;
|
1171
1294
|
this.advance();
|
@@ -1377,6 +1500,9 @@ function createMethodCall(object, method, args) {
|
|
1377
1500
|
function createEnumAccess(enumName, value) {
|
1378
1501
|
return { kind: "EnumAccess", enumName, value };
|
1379
1502
|
}
|
1503
|
+
function createArrayLiteral(elements) {
|
1504
|
+
return { kind: "ArrayLiteral", elements };
|
1505
|
+
}
|
1380
1506
|
|
1381
1507
|
// src/expression/Parser.ts
|
1382
1508
|
var ExpressionParser = class {
|
@@ -1673,6 +1799,17 @@ var ExpressionParser = class {
|
|
1673
1799
|
this.expect("RPAREN" /* RPAREN */, "Expected ) after expression");
|
1674
1800
|
return expr;
|
1675
1801
|
}
|
1802
|
+
if (this.match("LBRACKET" /* LBRACKET */)) {
|
1803
|
+
const elements = [];
|
1804
|
+
if (this.current().type !== "RBRACKET" /* RBRACKET */) {
|
1805
|
+
elements.push(this.parseTernary());
|
1806
|
+
while (this.match("COMMA" /* COMMA */)) {
|
1807
|
+
elements.push(this.parseTernary());
|
1808
|
+
}
|
1809
|
+
}
|
1810
|
+
this.expect("RBRACKET" /* RBRACKET */, "Expected ] after array literal");
|
1811
|
+
return createArrayLiteral(elements);
|
1812
|
+
}
|
1676
1813
|
throw new ParseError(
|
1677
1814
|
`Unexpected token: ${this.current().type}`,
|
1678
1815
|
this.current().position
|
@@ -1711,6 +1848,8 @@ var Evaluator = class {
|
|
1711
1848
|
return this.evaluateMethodCall(n.object, n.method, n.args, context);
|
1712
1849
|
case "EnumAccess":
|
1713
1850
|
return this.evaluateEnumAccess(n.enumName, n.value, context);
|
1851
|
+
case "ArrayLiteral":
|
1852
|
+
return this.evaluateArrayLiteral(n.elements, context);
|
1714
1853
|
default:
|
1715
1854
|
throw new ParseError(`Unknown AST node kind: ${node.kind}`);
|
1716
1855
|
}
|
@@ -1756,15 +1895,15 @@ var Evaluator = class {
|
|
1756
1895
|
return !this.equals(leftVal, rightVal);
|
1757
1896
|
// Bitwise
|
1758
1897
|
case "<<":
|
1759
|
-
return this.
|
1898
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a << b);
|
1760
1899
|
case ">>":
|
1761
|
-
return this.
|
1900
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a >> b);
|
1762
1901
|
case "&":
|
1763
|
-
return this.
|
1902
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a & b);
|
1764
1903
|
case "|":
|
1765
|
-
return this.
|
1904
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a | b);
|
1766
1905
|
case "^":
|
1767
|
-
return this.
|
1906
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a ^ b);
|
1768
1907
|
// Logical
|
1769
1908
|
case "and":
|
1770
1909
|
return this.toBoolean(leftVal) && this.toBoolean(rightVal);
|
@@ -1808,6 +1947,16 @@ var Evaluator = class {
|
|
1808
1947
|
`Cannot access property ${property} of null/undefined`
|
1809
1948
|
);
|
1810
1949
|
}
|
1950
|
+
if (property === "to_i") {
|
1951
|
+
if (typeof obj === "number") return Math.floor(obj);
|
1952
|
+
if (typeof obj === "bigint") return Number(obj);
|
1953
|
+
if (typeof obj === "string") return parseInt(obj, 10);
|
1954
|
+
if (typeof obj === "boolean") return obj ? 1 : 0;
|
1955
|
+
return this.toInt(obj);
|
1956
|
+
}
|
1957
|
+
if (property === "to_s") {
|
1958
|
+
return String(obj);
|
1959
|
+
}
|
1811
1960
|
if (typeof obj === "object") {
|
1812
1961
|
return obj[property];
|
1813
1962
|
}
|
@@ -1834,8 +1983,9 @@ var Evaluator = class {
|
|
1834
1983
|
* Evaluate method call (object.method()).
|
1835
1984
|
* @private
|
1836
1985
|
*/
|
1837
|
-
evaluateMethodCall(object, method,
|
1986
|
+
evaluateMethodCall(object, method, args, context) {
|
1838
1987
|
const obj = this.evaluate(object, context);
|
1988
|
+
const evalArgs = args.map((arg) => this.evaluate(arg, context));
|
1839
1989
|
if (method === "length" || method === "size") {
|
1840
1990
|
if (Array.isArray(obj)) return obj.length;
|
1841
1991
|
if (obj instanceof Uint8Array) return obj.length;
|
@@ -1843,13 +1993,182 @@ var Evaluator = class {
|
|
1843
1993
|
throw new ParseError(`Object does not have a ${method} property`);
|
1844
1994
|
}
|
1845
1995
|
if (method === "to_i") {
|
1996
|
+
const base = evalArgs.length > 0 ? this.toInt(evalArgs[0]) : 10;
|
1997
|
+
if (typeof obj === "string") {
|
1998
|
+
return parseInt(obj, base);
|
1999
|
+
}
|
1846
2000
|
return this.toInt(obj);
|
1847
2001
|
}
|
1848
2002
|
if (method === "to_s") {
|
1849
2003
|
return String(obj);
|
1850
2004
|
}
|
2005
|
+
if (typeof obj === "string") {
|
2006
|
+
return this.evaluateStringMethod(obj, method, evalArgs);
|
2007
|
+
}
|
2008
|
+
if (Array.isArray(obj) || obj instanceof Uint8Array) {
|
2009
|
+
return this.evaluateArrayMethod(obj, method, evalArgs);
|
2010
|
+
}
|
1851
2011
|
throw new ParseError(`Unknown method: ${method}`);
|
1852
2012
|
}
|
2013
|
+
/**
|
2014
|
+
* Evaluate string methods.
|
2015
|
+
* @private
|
2016
|
+
*/
|
2017
|
+
evaluateStringMethod(str, method, args) {
|
2018
|
+
switch (method) {
|
2019
|
+
case "substring": {
|
2020
|
+
const start = args.length > 0 ? this.toInt(args[0]) : 0;
|
2021
|
+
const end = args.length > 1 ? this.toInt(args[1]) : void 0;
|
2022
|
+
return str.substring(start, end);
|
2023
|
+
}
|
2024
|
+
case "substr": {
|
2025
|
+
const start = args.length > 0 ? this.toInt(args[0]) : 0;
|
2026
|
+
const length = args.length > 1 ? this.toInt(args[1]) : void 0;
|
2027
|
+
return str.substr(start, length);
|
2028
|
+
}
|
2029
|
+
case "reverse":
|
2030
|
+
return str.split("").reverse().join("");
|
2031
|
+
case "to_i": {
|
2032
|
+
const base = args.length > 0 ? this.toInt(args[0]) : 10;
|
2033
|
+
return parseInt(str, base);
|
2034
|
+
}
|
2035
|
+
case "length":
|
2036
|
+
case "size":
|
2037
|
+
return str.length;
|
2038
|
+
// Ruby-style string methods used in Kaitai
|
2039
|
+
case "upcase":
|
2040
|
+
case "to_upper":
|
2041
|
+
return str.toUpperCase();
|
2042
|
+
case "downcase":
|
2043
|
+
case "to_lower":
|
2044
|
+
return str.toLowerCase();
|
2045
|
+
case "capitalize":
|
2046
|
+
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
2047
|
+
case "strip":
|
2048
|
+
case "trim":
|
2049
|
+
return str.trim();
|
2050
|
+
case "lstrip":
|
2051
|
+
case "trim_start":
|
2052
|
+
return str.trimStart();
|
2053
|
+
case "rstrip":
|
2054
|
+
case "trim_end":
|
2055
|
+
return str.trimEnd();
|
2056
|
+
case "starts_with":
|
2057
|
+
case "startsWith": {
|
2058
|
+
if (args.length === 0) {
|
2059
|
+
throw new ParseError("starts_with requires 1 argument");
|
2060
|
+
}
|
2061
|
+
return str.startsWith(String(args[0]));
|
2062
|
+
}
|
2063
|
+
case "ends_with":
|
2064
|
+
case "endsWith": {
|
2065
|
+
if (args.length === 0) {
|
2066
|
+
throw new ParseError("ends_with requires 1 argument");
|
2067
|
+
}
|
2068
|
+
return str.endsWith(String(args[0]));
|
2069
|
+
}
|
2070
|
+
case "includes":
|
2071
|
+
case "contains": {
|
2072
|
+
if (args.length === 0) {
|
2073
|
+
throw new ParseError("includes requires 1 argument");
|
2074
|
+
}
|
2075
|
+
return str.includes(String(args[0]));
|
2076
|
+
}
|
2077
|
+
case "index_of":
|
2078
|
+
case "indexOf": {
|
2079
|
+
if (args.length === 0) {
|
2080
|
+
throw new ParseError("index_of requires 1 argument");
|
2081
|
+
}
|
2082
|
+
return str.indexOf(String(args[0]));
|
2083
|
+
}
|
2084
|
+
case "split": {
|
2085
|
+
if (args.length === 0) {
|
2086
|
+
throw new ParseError("split requires 1 argument");
|
2087
|
+
}
|
2088
|
+
return str.split(String(args[0]));
|
2089
|
+
}
|
2090
|
+
case "replace": {
|
2091
|
+
if (args.length < 2) {
|
2092
|
+
throw new ParseError("replace requires 2 arguments");
|
2093
|
+
}
|
2094
|
+
return str.replace(String(args[0]), String(args[1]));
|
2095
|
+
}
|
2096
|
+
case "replace_all":
|
2097
|
+
case "replaceAll": {
|
2098
|
+
if (args.length < 2) {
|
2099
|
+
throw new ParseError("replace_all requires 2 arguments");
|
2100
|
+
}
|
2101
|
+
const search = String(args[0]);
|
2102
|
+
const replace = String(args[1]);
|
2103
|
+
return str.split(search).join(replace);
|
2104
|
+
}
|
2105
|
+
case "pad_left":
|
2106
|
+
case "padStart": {
|
2107
|
+
if (args.length === 0) {
|
2108
|
+
throw new ParseError("pad_left requires at least 1 argument");
|
2109
|
+
}
|
2110
|
+
const length = this.toInt(args[0]);
|
2111
|
+
const fillString = args.length > 1 ? String(args[1]) : " ";
|
2112
|
+
return str.padStart(length, fillString);
|
2113
|
+
}
|
2114
|
+
case "pad_right":
|
2115
|
+
case "padEnd": {
|
2116
|
+
if (args.length === 0) {
|
2117
|
+
throw new ParseError("pad_right requires at least 1 argument");
|
2118
|
+
}
|
2119
|
+
const length = this.toInt(args[0]);
|
2120
|
+
const fillString = args.length > 1 ? String(args[1]) : " ";
|
2121
|
+
return str.padEnd(length, fillString);
|
2122
|
+
}
|
2123
|
+
default:
|
2124
|
+
throw new ParseError(`Unknown string method: ${method}`);
|
2125
|
+
}
|
2126
|
+
}
|
2127
|
+
/**
|
2128
|
+
* Evaluate array methods.
|
2129
|
+
* @private
|
2130
|
+
*/
|
2131
|
+
evaluateArrayMethod(arr, method, args) {
|
2132
|
+
const array = Array.isArray(arr) ? arr : Array.from(arr);
|
2133
|
+
switch (method) {
|
2134
|
+
case "length":
|
2135
|
+
case "size":
|
2136
|
+
return array.length;
|
2137
|
+
case "first":
|
2138
|
+
return array[0];
|
2139
|
+
case "last":
|
2140
|
+
return array[array.length - 1];
|
2141
|
+
case "min":
|
2142
|
+
return Math.min(...array.map((v) => this.toNumber(v)));
|
2143
|
+
case "max":
|
2144
|
+
return Math.max(...array.map((v) => this.toNumber(v)));
|
2145
|
+
case "reverse":
|
2146
|
+
return [...array].reverse();
|
2147
|
+
case "sort":
|
2148
|
+
return [...array].sort((a, b) => this.compare(a, b));
|
2149
|
+
case "includes":
|
2150
|
+
case "contains": {
|
2151
|
+
if (args.length === 0) {
|
2152
|
+
throw new ParseError("includes requires 1 argument");
|
2153
|
+
}
|
2154
|
+
return array.some((item) => this.equals(item, args[0]));
|
2155
|
+
}
|
2156
|
+
case "index_of":
|
2157
|
+
case "indexOf": {
|
2158
|
+
if (args.length === 0) {
|
2159
|
+
throw new ParseError("index_of requires 1 argument");
|
2160
|
+
}
|
2161
|
+
return array.findIndex((item) => this.equals(item, args[0]));
|
2162
|
+
}
|
2163
|
+
case "slice": {
|
2164
|
+
const start = args.length > 0 ? this.toInt(args[0]) : 0;
|
2165
|
+
const end = args.length > 1 ? this.toInt(args[1]) : void 0;
|
2166
|
+
return array.slice(start, end);
|
2167
|
+
}
|
2168
|
+
default:
|
2169
|
+
throw new ParseError(`Unknown array method: ${method}`);
|
2170
|
+
}
|
2171
|
+
}
|
1853
2172
|
/**
|
1854
2173
|
* Evaluate enum access (EnumName::value).
|
1855
2174
|
* @private
|
@@ -1879,6 +2198,38 @@ var Evaluator = class {
|
|
1879
2198
|
const result = a % b;
|
1880
2199
|
return result < 0 ? result + b : result;
|
1881
2200
|
}
|
2201
|
+
/**
|
2202
|
+
* Helper: Bitwise operation with BigInt support.
|
2203
|
+
* JavaScript bitwise operators work on 32-bit integers, but Kaitai
|
2204
|
+
* may use 64-bit values. For values that fit in 32 bits, use native ops.
|
2205
|
+
* For larger values, convert to BigInt (with limitations).
|
2206
|
+
* @private
|
2207
|
+
*/
|
2208
|
+
bitwiseOp(left, right, op) {
|
2209
|
+
if (typeof left === "bigint" || typeof right === "bigint") {
|
2210
|
+
const leftBig = typeof left === "bigint" ? left : BigInt(left);
|
2211
|
+
const rightBig = typeof right === "bigint" ? right : BigInt(right);
|
2212
|
+
if (op.toString().includes("<<")) {
|
2213
|
+
return leftBig << BigInt(Number(rightBig));
|
2214
|
+
}
|
2215
|
+
if (op.toString().includes(">>")) {
|
2216
|
+
return leftBig >> BigInt(Number(rightBig));
|
2217
|
+
}
|
2218
|
+
if (op.toString().includes("&")) {
|
2219
|
+
return leftBig & rightBig;
|
2220
|
+
}
|
2221
|
+
if (op.toString().includes("|")) {
|
2222
|
+
return leftBig | rightBig;
|
2223
|
+
}
|
2224
|
+
if (op.toString().includes("^")) {
|
2225
|
+
return leftBig ^ rightBig;
|
2226
|
+
}
|
2227
|
+
}
|
2228
|
+
if (left === void 0 || left === null || right === void 0 || right === null) {
|
2229
|
+
throw new ParseError("Cannot perform bitwise operation on null/undefined");
|
2230
|
+
}
|
2231
|
+
return op(this.toInt(left), this.toInt(right));
|
2232
|
+
}
|
1882
2233
|
/**
|
1883
2234
|
* Helper: Compare two values.
|
1884
2235
|
* @private
|
@@ -1899,8 +2250,30 @@ var Evaluator = class {
|
|
1899
2250
|
if (typeof left === "bigint" || typeof right === "bigint") {
|
1900
2251
|
return BigInt(left) === BigInt(right);
|
1901
2252
|
}
|
2253
|
+
const toArray = (v) => {
|
2254
|
+
if (Array.isArray(v))
|
2255
|
+
return v.map((x) => typeof x === "bigint" ? Number(x) : x);
|
2256
|
+
if (v instanceof Uint8Array) return Array.from(v);
|
2257
|
+
return null;
|
2258
|
+
};
|
2259
|
+
const leftArr = toArray(left);
|
2260
|
+
const rightArr = toArray(right);
|
2261
|
+
if (leftArr && rightArr) {
|
2262
|
+
if (leftArr.length !== rightArr.length) return false;
|
2263
|
+
for (let i = 0; i < leftArr.length; i++) {
|
2264
|
+
if (leftArr[i] !== rightArr[i]) return false;
|
2265
|
+
}
|
2266
|
+
return true;
|
2267
|
+
}
|
1902
2268
|
return left === right;
|
1903
2269
|
}
|
2270
|
+
/**
|
2271
|
+
* Evaluate an array literal.
|
2272
|
+
* @private
|
2273
|
+
*/
|
2274
|
+
evaluateArrayLiteral(elements, context) {
|
2275
|
+
return elements.map((e) => this.evaluate(e, context));
|
2276
|
+
}
|
1904
2277
|
/**
|
1905
2278
|
* Helper: Convert to number.
|
1906
2279
|
* @private
|
@@ -1935,7 +2308,8 @@ var Evaluator = class {
|
|
1935
2308
|
|
1936
2309
|
// src/expression/index.ts
|
1937
2310
|
function evaluateExpression(expression, context) {
|
1938
|
-
const
|
2311
|
+
const preprocessed = expression.replace(/\.as<[^>]+>/g, "");
|
2312
|
+
const lexer = new Lexer(preprocessed);
|
1939
2313
|
const tokens = lexer.tokenize();
|
1940
2314
|
const parser = new ExpressionParser(tokens);
|
1941
2315
|
const ast = parser.parse();
|
@@ -1943,6 +2317,112 @@ function evaluateExpression(expression, context) {
|
|
1943
2317
|
return evaluator.evaluate(ast, context);
|
1944
2318
|
}
|
1945
2319
|
|
2320
|
+
// src/utils/process.ts
|
2321
|
+
var import_pako = require("pako");
|
2322
|
+
function applyProcess(data, process) {
|
2323
|
+
const spec = typeof process === "string" ? { algorithm: process } : process;
|
2324
|
+
const algorithm = spec.algorithm;
|
2325
|
+
if (!algorithm) {
|
2326
|
+
throw new ParseError("Process specification missing algorithm");
|
2327
|
+
}
|
2328
|
+
switch (algorithm) {
|
2329
|
+
case "zlib":
|
2330
|
+
return processZlib(data);
|
2331
|
+
case "xor":
|
2332
|
+
return processXor(data, spec.key);
|
2333
|
+
case "rol":
|
2334
|
+
return processRol(data, spec.amount, spec.group);
|
2335
|
+
case "ror":
|
2336
|
+
return processRor(data, spec.amount, spec.group);
|
2337
|
+
case "bswap2":
|
2338
|
+
return processByteswap(data, 2);
|
2339
|
+
case "bswap4":
|
2340
|
+
return processByteswap(data, 4);
|
2341
|
+
case "bswap8":
|
2342
|
+
return processByteswap(data, 8);
|
2343
|
+
case "bswap16":
|
2344
|
+
return processByteswap(data, 16);
|
2345
|
+
default:
|
2346
|
+
throw new ParseError(
|
2347
|
+
`Unknown process algorithm: ${algorithm}. Supported: zlib, xor, rol, ror, bswap2, bswap4, bswap8, bswap16`
|
2348
|
+
);
|
2349
|
+
}
|
2350
|
+
}
|
2351
|
+
function processZlib(data) {
|
2352
|
+
try {
|
2353
|
+
return (0, import_pako.inflate)(data);
|
2354
|
+
} catch (error) {
|
2355
|
+
throw new ParseError(
|
2356
|
+
`Zlib decompression failed: ${error instanceof Error ? error.message : String(error)}`
|
2357
|
+
);
|
2358
|
+
}
|
2359
|
+
}
|
2360
|
+
function processXor(data, key) {
|
2361
|
+
if (key === void 0) {
|
2362
|
+
throw new ParseError("XOR process requires a key parameter");
|
2363
|
+
}
|
2364
|
+
const result = new Uint8Array(data.length);
|
2365
|
+
const keyBytes = Array.isArray(key) ? key : [key];
|
2366
|
+
if (keyBytes.length === 0) {
|
2367
|
+
throw new ParseError("XOR key cannot be empty");
|
2368
|
+
}
|
2369
|
+
for (let i = 0; i < data.length; i++) {
|
2370
|
+
result[i] = data[i] ^ keyBytes[i % keyBytes.length];
|
2371
|
+
}
|
2372
|
+
return result;
|
2373
|
+
}
|
2374
|
+
function processRol(data, amount, group) {
|
2375
|
+
const bits = amount ?? 1;
|
2376
|
+
const groupSize = group ?? 1;
|
2377
|
+
if (bits < 0 || bits > 7) {
|
2378
|
+
throw new ParseError("ROL amount must be between 0 and 7");
|
2379
|
+
}
|
2380
|
+
if (groupSize !== 1) {
|
2381
|
+
throw new ParseError("ROL with group size > 1 not yet supported");
|
2382
|
+
}
|
2383
|
+
const result = new Uint8Array(data.length);
|
2384
|
+
for (let i = 0; i < data.length; i++) {
|
2385
|
+
const byte = data[i];
|
2386
|
+
result[i] = (byte << bits | byte >> 8 - bits) & 255;
|
2387
|
+
}
|
2388
|
+
return result;
|
2389
|
+
}
|
2390
|
+
function processRor(data, amount, group) {
|
2391
|
+
const bits = amount ?? 1;
|
2392
|
+
const groupSize = group ?? 1;
|
2393
|
+
if (bits < 0 || bits > 7) {
|
2394
|
+
throw new ParseError("ROR amount must be between 0 and 7");
|
2395
|
+
}
|
2396
|
+
if (groupSize !== 1) {
|
2397
|
+
throw new ParseError("ROR with group size > 1 not yet supported");
|
2398
|
+
}
|
2399
|
+
const result = new Uint8Array(data.length);
|
2400
|
+
for (let i = 0; i < data.length; i++) {
|
2401
|
+
const byte = data[i];
|
2402
|
+
result[i] = (byte >> bits | byte << 8 - bits) & 255;
|
2403
|
+
}
|
2404
|
+
return result;
|
2405
|
+
}
|
2406
|
+
function processByteswap(data, groupSize) {
|
2407
|
+
if (![2, 4, 8, 16].includes(groupSize)) {
|
2408
|
+
throw new ParseError(
|
2409
|
+
`Invalid byteswap group size: ${groupSize}. Must be 2, 4, 8, or 16`
|
2410
|
+
);
|
2411
|
+
}
|
2412
|
+
if (data.length % groupSize !== 0) {
|
2413
|
+
throw new ParseError(
|
2414
|
+
`Data length ${data.length} is not aligned to group size ${groupSize}`
|
2415
|
+
);
|
2416
|
+
}
|
2417
|
+
const result = new Uint8Array(data.length);
|
2418
|
+
for (let i = 0; i < data.length; i += groupSize) {
|
2419
|
+
for (let j = 0; j < groupSize; j++) {
|
2420
|
+
result[i + j] = data[i + groupSize - 1 - j];
|
2421
|
+
}
|
2422
|
+
}
|
2423
|
+
return result;
|
2424
|
+
}
|
2425
|
+
|
1946
2426
|
// src/interpreter/TypeInterpreter.ts
|
1947
2427
|
var TypeInterpreter = class _TypeInterpreter {
|
1948
2428
|
/**
|
@@ -1961,18 +2441,38 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
1961
2441
|
throw new ParseError("Root schema must have meta.id");
|
1962
2442
|
}
|
1963
2443
|
}
|
2444
|
+
/**
|
2445
|
+
* Safely extract a KaitaiStream from an object that may expose `_io`.
|
2446
|
+
* Avoids using `any` casts to satisfy linting.
|
2447
|
+
*/
|
2448
|
+
static getKaitaiIO(val) {
|
2449
|
+
if (val && typeof val === "object") {
|
2450
|
+
const rec = val;
|
2451
|
+
const maybe = rec["_io"];
|
2452
|
+
if (maybe instanceof KaitaiStream) return maybe;
|
2453
|
+
}
|
2454
|
+
return null;
|
2455
|
+
}
|
1964
2456
|
/**
|
1965
2457
|
* Parse binary data according to the schema.
|
1966
2458
|
*
|
1967
2459
|
* @param stream - Binary stream to parse
|
1968
2460
|
* @param parent - Parent object (for nested types)
|
1969
2461
|
* @param typeArgs - Arguments for parametric types
|
2462
|
+
* @param root - Root object of the parse tree (for nested types)
|
1970
2463
|
* @returns Parsed object
|
1971
2464
|
*/
|
1972
|
-
parse(stream, parent, typeArgs) {
|
2465
|
+
parse(stream, parent, typeArgs, root) {
|
1973
2466
|
const result = {};
|
1974
|
-
const
|
2467
|
+
const actualRoot = root || result;
|
2468
|
+
const context = new Context(stream, actualRoot, parent, this.schema.enums);
|
1975
2469
|
context.current = result;
|
2470
|
+
const startPos = stream.pos;
|
2471
|
+
result["_io"] = stream;
|
2472
|
+
if (root) {
|
2473
|
+
;
|
2474
|
+
result["_root"] = root;
|
2475
|
+
}
|
1976
2476
|
if (typeArgs && this.schema.params) {
|
1977
2477
|
for (let i = 0; i < this.schema.params.length && i < typeArgs.length; i++) {
|
1978
2478
|
const param = this.schema.params[i];
|
@@ -1981,6 +2481,9 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
1981
2481
|
context.set(param.id, evaluatedArg);
|
1982
2482
|
}
|
1983
2483
|
}
|
2484
|
+
if (this.schema.instances) {
|
2485
|
+
this.setupInstances(result, stream, context);
|
2486
|
+
}
|
1984
2487
|
if (this.schema.seq) {
|
1985
2488
|
for (const attr of this.schema.seq) {
|
1986
2489
|
const value = this.parseAttribute(attr, context);
|
@@ -1989,9 +2492,8 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
1989
2492
|
}
|
1990
2493
|
}
|
1991
2494
|
}
|
1992
|
-
|
1993
|
-
|
1994
|
-
}
|
2495
|
+
const endPos = stream.pos;
|
2496
|
+
result["_sizeof"] = endPos - startPos;
|
1995
2497
|
return result;
|
1996
2498
|
}
|
1997
2499
|
/**
|
@@ -2075,7 +2577,22 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2075
2577
|
* @private
|
2076
2578
|
*/
|
2077
2579
|
parseAttribute(attr, context) {
|
2078
|
-
|
2580
|
+
let stream = context.io;
|
2581
|
+
if (attr.io !== void 0) {
|
2582
|
+
const ioVal = this.evaluateValue(attr.io, context);
|
2583
|
+
if (ioVal instanceof KaitaiStream) {
|
2584
|
+
stream = ioVal;
|
2585
|
+
} else {
|
2586
|
+
const kio = _TypeInterpreter.getKaitaiIO(ioVal);
|
2587
|
+
if (kio) {
|
2588
|
+
stream = kio;
|
2589
|
+
} else {
|
2590
|
+
throw new ParseError(
|
2591
|
+
"io must evaluate to a KaitaiStream or an object with _io"
|
2592
|
+
);
|
2593
|
+
}
|
2594
|
+
}
|
2595
|
+
}
|
2079
2596
|
if (attr.if) {
|
2080
2597
|
const condition = this.evaluateValue(attr.if, context);
|
2081
2598
|
if (!condition) {
|
@@ -2092,9 +2609,6 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2092
2609
|
throw new ParseError(`pos must evaluate to a number, got ${typeof pos}`);
|
2093
2610
|
}
|
2094
2611
|
}
|
2095
|
-
if (attr.io) {
|
2096
|
-
throw new NotImplementedError("Custom I/O streams");
|
2097
|
-
}
|
2098
2612
|
if (attr.repeat) {
|
2099
2613
|
return this.parseRepeated(attr, context);
|
2100
2614
|
}
|
@@ -2191,7 +2705,7 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2191
2705
|
}
|
2192
2706
|
return bytes;
|
2193
2707
|
} else {
|
2194
|
-
const encoding = attr.encoding || this.schema.meta.encoding || "UTF-8";
|
2708
|
+
const encoding = attr.encoding || this.schema.meta?.encoding || this.parentMeta?.encoding || "UTF-8";
|
2195
2709
|
const str = stream.readStr(expected.length, encoding);
|
2196
2710
|
if (str !== expected) {
|
2197
2711
|
throw new ValidationError(
|
@@ -2220,7 +2734,7 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2220
2734
|
throw new ParseError(`size must be non-negative, got ${size}`);
|
2221
2735
|
}
|
2222
2736
|
if (type === "str" || !type) {
|
2223
|
-
const encoding = attr.encoding || this.schema.meta.encoding || "UTF-8";
|
2737
|
+
const encoding = attr.encoding || this.schema.meta?.encoding || this.parentMeta?.encoding || "UTF-8";
|
2224
2738
|
let data;
|
2225
2739
|
if (type === "str") {
|
2226
2740
|
data = stream.readBytes(size);
|
@@ -2246,11 +2760,19 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2246
2760
|
}
|
2247
2761
|
if (attr["size-eos"]) {
|
2248
2762
|
if (type === "str") {
|
2249
|
-
const encoding = attr.encoding || this.schema.meta.encoding || "UTF-8";
|
2763
|
+
const encoding = attr.encoding || this.schema.meta?.encoding || this.parentMeta?.encoding || "UTF-8";
|
2250
2764
|
const bytes = stream.readBytesFull();
|
2251
2765
|
return new TextDecoder(encoding).decode(bytes);
|
2252
2766
|
} else {
|
2253
|
-
|
2767
|
+
let bytes = stream.readBytesFull();
|
2768
|
+
if (attr.process) {
|
2769
|
+
bytes = this.applyProcessing(bytes, attr.process);
|
2770
|
+
}
|
2771
|
+
if (type) {
|
2772
|
+
const sub = new KaitaiStream(bytes);
|
2773
|
+
return this.parseType(type, sub, context, attr["type-args"]);
|
2774
|
+
}
|
2775
|
+
return bytes;
|
2254
2776
|
}
|
2255
2777
|
}
|
2256
2778
|
if (!type) {
|
@@ -2276,11 +2798,13 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2276
2798
|
context
|
2277
2799
|
);
|
2278
2800
|
}
|
2279
|
-
|
2280
|
-
|
2801
|
+
const { typeName, args } = this.parseParameterizedType(type, context);
|
2802
|
+
const effectiveArgs = args.length > 0 ? args : typeArgs;
|
2803
|
+
if (isBuiltinType(typeName)) {
|
2804
|
+
return this.parseBuiltinType(typeName, stream, context);
|
2281
2805
|
}
|
2282
|
-
if (this.schema.types &&
|
2283
|
-
const typeSchema = this.schema.types[
|
2806
|
+
if (this.schema.types && typeName in this.schema.types) {
|
2807
|
+
const typeSchema = this.schema.types[typeName];
|
2284
2808
|
const meta = this.schema.meta || this.parentMeta;
|
2285
2809
|
if (this.schema.enums && !typeSchema.enums) {
|
2286
2810
|
typeSchema.enums = this.schema.enums;
|
@@ -2289,9 +2813,100 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2289
2813
|
typeSchema.types = this.schema.types;
|
2290
2814
|
}
|
2291
2815
|
const interpreter = new _TypeInterpreter(typeSchema, meta);
|
2292
|
-
return interpreter.parse(
|
2816
|
+
return interpreter.parse(
|
2817
|
+
stream,
|
2818
|
+
context.current,
|
2819
|
+
effectiveArgs,
|
2820
|
+
context.root
|
2821
|
+
);
|
2822
|
+
}
|
2823
|
+
throw new ParseError(`Unknown type: ${typeName}`);
|
2824
|
+
}
|
2825
|
+
/**
|
2826
|
+
* Parse parameterized type syntax and extract type name and arguments.
|
2827
|
+
* Supports: type_name(arg1, arg2, ...) or just type_name
|
2828
|
+
*
|
2829
|
+
* @param typeSpec - Type specification string
|
2830
|
+
* @param context - Execution context for evaluating argument expressions
|
2831
|
+
* @returns Object with typeName and evaluated args
|
2832
|
+
* @private
|
2833
|
+
*/
|
2834
|
+
parseParameterizedType(typeSpec, context) {
|
2835
|
+
const match = typeSpec.match(/^([a-z_][a-z0-9_]*)\((.*)\)$/i);
|
2836
|
+
if (!match) {
|
2837
|
+
return { typeName: typeSpec, args: [] };
|
2838
|
+
}
|
2839
|
+
const typeName = match[1];
|
2840
|
+
const argsString = match[2].trim();
|
2841
|
+
if (!argsString) {
|
2842
|
+
return { typeName, args: [] };
|
2843
|
+
}
|
2844
|
+
const args = [];
|
2845
|
+
let current = "";
|
2846
|
+
let inString = false;
|
2847
|
+
let stringChar = "";
|
2848
|
+
let parenDepth = 0;
|
2849
|
+
for (let i = 0; i < argsString.length; i++) {
|
2850
|
+
const char = argsString[i];
|
2851
|
+
if (inString) {
|
2852
|
+
current += char;
|
2853
|
+
if (char === stringChar && argsString[i - 1] !== "\\") {
|
2854
|
+
inString = false;
|
2855
|
+
}
|
2856
|
+
} else if (char === '"' || char === "'") {
|
2857
|
+
inString = true;
|
2858
|
+
stringChar = char;
|
2859
|
+
current += char;
|
2860
|
+
} else if (char === "(") {
|
2861
|
+
parenDepth++;
|
2862
|
+
current += char;
|
2863
|
+
} else if (char === ")") {
|
2864
|
+
parenDepth--;
|
2865
|
+
current += char;
|
2866
|
+
} else if (char === "," && parenDepth === 0) {
|
2867
|
+
args.push(this.parseArgument(current.trim(), context));
|
2868
|
+
current = "";
|
2869
|
+
} else {
|
2870
|
+
current += char;
|
2871
|
+
}
|
2872
|
+
}
|
2873
|
+
if (current.trim()) {
|
2874
|
+
args.push(this.parseArgument(current.trim(), context));
|
2875
|
+
}
|
2876
|
+
return { typeName, args };
|
2877
|
+
}
|
2878
|
+
/**
|
2879
|
+
* Parse and evaluate a single type argument.
|
2880
|
+
*
|
2881
|
+
* @param arg - Argument string
|
2882
|
+
* @param context - Execution context
|
2883
|
+
* @returns Evaluated argument value
|
2884
|
+
* @private
|
2885
|
+
*/
|
2886
|
+
parseArgument(arg, context) {
|
2887
|
+
if (arg === "true") return true;
|
2888
|
+
if (arg === "false") return false;
|
2889
|
+
if (arg.startsWith('"') && arg.endsWith('"') || arg.startsWith("'") && arg.endsWith("'")) {
|
2890
|
+
return arg.slice(1, -1);
|
2891
|
+
}
|
2892
|
+
if (/^-?\d+$/.test(arg)) {
|
2893
|
+
return parseInt(arg, 10);
|
2894
|
+
}
|
2895
|
+
if (/^-?\d+\.\d+$/.test(arg)) {
|
2896
|
+
return parseFloat(arg);
|
2897
|
+
}
|
2898
|
+
if (/^0x[0-9a-f]+$/i.test(arg)) {
|
2899
|
+
return parseInt(arg, 16);
|
2900
|
+
}
|
2901
|
+
try {
|
2902
|
+
const result = this.evaluateValue(arg, context);
|
2903
|
+
if (typeof result === "string" || typeof result === "number" || typeof result === "boolean") {
|
2904
|
+
return result;
|
2905
|
+
}
|
2906
|
+
return Number(result);
|
2907
|
+
} catch {
|
2908
|
+
return arg;
|
2293
2909
|
}
|
2294
|
-
throw new ParseError(`Unknown type: ${type}`);
|
2295
2910
|
}
|
2296
2911
|
/**
|
2297
2912
|
* Parse a switch type (type selection based on expression).
|
@@ -2334,18 +2949,33 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2334
2949
|
* @returns Parsed value
|
2335
2950
|
* @private
|
2336
2951
|
*/
|
2337
|
-
parseBuiltinType(type, stream,
|
2952
|
+
parseBuiltinType(type, stream, context) {
|
2338
2953
|
const base = getBaseType(type);
|
2339
2954
|
const typeEndian = getTypeEndianness(type);
|
2340
2955
|
const meta = this.schema.meta || this.parentMeta;
|
2341
2956
|
const metaEndian = meta?.endian;
|
2342
|
-
|
2957
|
+
let endian;
|
2958
|
+
if (typeEndian) {
|
2959
|
+
endian = typeEndian;
|
2960
|
+
} else if (typeof metaEndian === "string") {
|
2961
|
+
endian = metaEndian;
|
2962
|
+
} else if (metaEndian && typeof metaEndian === "object") {
|
2963
|
+
endian = this.evaluateEndianExpression(metaEndian, context);
|
2964
|
+
} else {
|
2965
|
+
endian = "le";
|
2966
|
+
}
|
2343
2967
|
if (isIntegerType(type)) {
|
2344
2968
|
return this.readInteger(base, endian, stream);
|
2345
2969
|
}
|
2346
2970
|
if (isFloatType(type)) {
|
2347
2971
|
return this.readFloat(base, endian, stream);
|
2348
2972
|
}
|
2973
|
+
if (/^b\d+$/.test(type)) {
|
2974
|
+
const n = parseInt(type.slice(1), 10);
|
2975
|
+
const val = stream.readBitsIntBe(n);
|
2976
|
+
const maxSafe = BigInt(Number.MAX_SAFE_INTEGER);
|
2977
|
+
return val <= maxSafe ? Number(val) : val;
|
2978
|
+
}
|
2349
2979
|
if (isStringType(type)) {
|
2350
2980
|
const encoding = this.schema.meta?.encoding || "UTF-8";
|
2351
2981
|
if (type === "strz") {
|
@@ -2410,7 +3040,7 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2410
3040
|
}
|
2411
3041
|
/**
|
2412
3042
|
* Apply processing transformation to data.
|
2413
|
-
*
|
3043
|
+
* Delegates to the process utility module.
|
2414
3044
|
*
|
2415
3045
|
* @param data - Data to process
|
2416
3046
|
* @param process - Processing specification
|
@@ -2418,13 +3048,35 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2418
3048
|
* @private
|
2419
3049
|
*/
|
2420
3050
|
applyProcessing(data, process) {
|
2421
|
-
|
2422
|
-
|
2423
|
-
|
2424
|
-
|
2425
|
-
|
3051
|
+
return applyProcess(data, process);
|
3052
|
+
}
|
3053
|
+
/**
|
3054
|
+
* Evaluate expression-based endianness (switch-on).
|
3055
|
+
*
|
3056
|
+
* @param endianExpr - Endianness expression with switch-on and cases
|
3057
|
+
* @param context - Execution context
|
3058
|
+
* @returns Resolved endianness ('le' or 'be')
|
3059
|
+
* @private
|
3060
|
+
*/
|
3061
|
+
evaluateEndianExpression(endianExpr, context) {
|
3062
|
+
const switchOn = endianExpr["switch-on"];
|
3063
|
+
const cases = endianExpr.cases;
|
3064
|
+
if (!switchOn || typeof switchOn !== "string") {
|
3065
|
+
throw new ParseError('Endian expression missing "switch-on" field');
|
3066
|
+
}
|
3067
|
+
if (!cases || typeof cases !== "object") {
|
3068
|
+
throw new ParseError('Endian expression missing "cases" field');
|
3069
|
+
}
|
3070
|
+
const switchValue = this.evaluateValue(switchOn, context);
|
3071
|
+
const key = String(switchValue);
|
3072
|
+
if (key in cases) {
|
3073
|
+
const endian = cases[key];
|
3074
|
+
if (endian !== "le" && endian !== "be") {
|
3075
|
+
throw new ParseError(`Invalid endianness value: ${endian}`);
|
3076
|
+
}
|
3077
|
+
return endian;
|
2426
3078
|
}
|
2427
|
-
return
|
3079
|
+
return "le";
|
2428
3080
|
}
|
2429
3081
|
/**
|
2430
3082
|
* Evaluate a value that can be an expression or literal.
|
@@ -2567,6 +3219,12 @@ function parse(ksyYaml, buffer, options = {}) {
|
|
2567
3219
|
* @author Fabiano Pinto
|
2568
3220
|
* @license MIT
|
2569
3221
|
*/
|
3222
|
+
/**
|
3223
|
+
* @fileoverview Data processing utilities for Kaitai Struct
|
3224
|
+
* @module utils/process
|
3225
|
+
* @author Fabiano Pinto
|
3226
|
+
* @license MIT
|
3227
|
+
*/
|
2570
3228
|
/**
|
2571
3229
|
* @fileoverview Type interpreter for executing Kaitai Struct schemas
|
2572
3230
|
* @module interpreter/TypeInterpreter
|