@k67/kaitai-struct-ts 0.9.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 +568 -53
- package/dist/index.d.mts +46 -6
- package/dist/index.d.ts +46 -6
- package/dist/index.js +529 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +529 -34
- 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
|
}
|
@@ -1815,15 +1853,15 @@ var Evaluator = class {
|
|
1815
1853
|
return !this.equals(leftVal, rightVal);
|
1816
1854
|
// Bitwise
|
1817
1855
|
case "<<":
|
1818
|
-
return this.
|
1856
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a << b);
|
1819
1857
|
case ">>":
|
1820
|
-
return this.
|
1858
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a >> b);
|
1821
1859
|
case "&":
|
1822
|
-
return this.
|
1860
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a & b);
|
1823
1861
|
case "|":
|
1824
|
-
return this.
|
1862
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a | b);
|
1825
1863
|
case "^":
|
1826
|
-
return this.
|
1864
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a ^ b);
|
1827
1865
|
// Logical
|
1828
1866
|
case "and":
|
1829
1867
|
return this.toBoolean(leftVal) && this.toBoolean(rightVal);
|
@@ -1867,6 +1905,16 @@ var Evaluator = class {
|
|
1867
1905
|
`Cannot access property ${property} of null/undefined`
|
1868
1906
|
);
|
1869
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
|
+
}
|
1870
1918
|
if (typeof obj === "object") {
|
1871
1919
|
return obj[property];
|
1872
1920
|
}
|
@@ -1893,8 +1941,9 @@ var Evaluator = class {
|
|
1893
1941
|
* Evaluate method call (object.method()).
|
1894
1942
|
* @private
|
1895
1943
|
*/
|
1896
|
-
evaluateMethodCall(object, method,
|
1944
|
+
evaluateMethodCall(object, method, args, context) {
|
1897
1945
|
const obj = this.evaluate(object, context);
|
1946
|
+
const evalArgs = args.map((arg) => this.evaluate(arg, context));
|
1898
1947
|
if (method === "length" || method === "size") {
|
1899
1948
|
if (Array.isArray(obj)) return obj.length;
|
1900
1949
|
if (obj instanceof Uint8Array) return obj.length;
|
@@ -1902,13 +1951,182 @@ var Evaluator = class {
|
|
1902
1951
|
throw new ParseError(`Object does not have a ${method} property`);
|
1903
1952
|
}
|
1904
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
|
+
}
|
1905
1958
|
return this.toInt(obj);
|
1906
1959
|
}
|
1907
1960
|
if (method === "to_s") {
|
1908
1961
|
return String(obj);
|
1909
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
|
+
}
|
1910
1969
|
throw new ParseError(`Unknown method: ${method}`);
|
1911
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
|
+
}
|
1912
2130
|
/**
|
1913
2131
|
* Evaluate enum access (EnumName::value).
|
1914
2132
|
* @private
|
@@ -1938,6 +2156,38 @@ var Evaluator = class {
|
|
1938
2156
|
const result = a % b;
|
1939
2157
|
return result < 0 ? result + b : result;
|
1940
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
|
+
}
|
1941
2191
|
/**
|
1942
2192
|
* Helper: Compare two values.
|
1943
2193
|
* @private
|
@@ -2025,6 +2275,112 @@ function evaluateExpression(expression, context) {
|
|
2025
2275
|
return evaluator.evaluate(ast, context);
|
2026
2276
|
}
|
2027
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
|
+
|
2028
2384
|
// src/interpreter/TypeInterpreter.ts
|
2029
2385
|
var TypeInterpreter = class _TypeInterpreter {
|
2030
2386
|
/**
|
@@ -2061,13 +2417,20 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2061
2417
|
* @param stream - Binary stream to parse
|
2062
2418
|
* @param parent - Parent object (for nested types)
|
2063
2419
|
* @param typeArgs - Arguments for parametric types
|
2420
|
+
* @param root - Root object of the parse tree (for nested types)
|
2064
2421
|
* @returns Parsed object
|
2065
2422
|
*/
|
2066
|
-
parse(stream, parent, typeArgs) {
|
2423
|
+
parse(stream, parent, typeArgs, root) {
|
2067
2424
|
const result = {};
|
2068
|
-
const
|
2425
|
+
const actualRoot = root || result;
|
2426
|
+
const context = new Context(stream, actualRoot, parent, this.schema.enums);
|
2069
2427
|
context.current = result;
|
2428
|
+
const startPos = stream.pos;
|
2070
2429
|
result["_io"] = stream;
|
2430
|
+
if (root) {
|
2431
|
+
;
|
2432
|
+
result["_root"] = root;
|
2433
|
+
}
|
2071
2434
|
if (typeArgs && this.schema.params) {
|
2072
2435
|
for (let i = 0; i < this.schema.params.length && i < typeArgs.length; i++) {
|
2073
2436
|
const param = this.schema.params[i];
|
@@ -2076,6 +2439,9 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2076
2439
|
context.set(param.id, evaluatedArg);
|
2077
2440
|
}
|
2078
2441
|
}
|
2442
|
+
if (this.schema.instances) {
|
2443
|
+
this.setupInstances(result, stream, context);
|
2444
|
+
}
|
2079
2445
|
if (this.schema.seq) {
|
2080
2446
|
for (const attr of this.schema.seq) {
|
2081
2447
|
const value = this.parseAttribute(attr, context);
|
@@ -2084,9 +2450,8 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2084
2450
|
}
|
2085
2451
|
}
|
2086
2452
|
}
|
2087
|
-
|
2088
|
-
|
2089
|
-
}
|
2453
|
+
const endPos = stream.pos;
|
2454
|
+
result["_sizeof"] = endPos - startPos;
|
2090
2455
|
return result;
|
2091
2456
|
}
|
2092
2457
|
/**
|
@@ -2391,11 +2756,13 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2391
2756
|
context
|
2392
2757
|
);
|
2393
2758
|
}
|
2394
|
-
|
2395
|
-
|
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);
|
2396
2763
|
}
|
2397
|
-
if (this.schema.types &&
|
2398
|
-
const typeSchema = this.schema.types[
|
2764
|
+
if (this.schema.types && typeName in this.schema.types) {
|
2765
|
+
const typeSchema = this.schema.types[typeName];
|
2399
2766
|
const meta = this.schema.meta || this.parentMeta;
|
2400
2767
|
if (this.schema.enums && !typeSchema.enums) {
|
2401
2768
|
typeSchema.enums = this.schema.enums;
|
@@ -2404,9 +2771,100 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2404
2771
|
typeSchema.types = this.schema.types;
|
2405
2772
|
}
|
2406
2773
|
const interpreter = new _TypeInterpreter(typeSchema, meta);
|
2407
|
-
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;
|
2408
2867
|
}
|
2409
|
-
throw new ParseError(`Unknown type: ${type}`);
|
2410
2868
|
}
|
2411
2869
|
/**
|
2412
2870
|
* Parse a switch type (type selection based on expression).
|
@@ -2449,12 +2907,21 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2449
2907
|
* @returns Parsed value
|
2450
2908
|
* @private
|
2451
2909
|
*/
|
2452
|
-
parseBuiltinType(type, stream,
|
2910
|
+
parseBuiltinType(type, stream, context) {
|
2453
2911
|
const base = getBaseType(type);
|
2454
2912
|
const typeEndian = getTypeEndianness(type);
|
2455
2913
|
const meta = this.schema.meta || this.parentMeta;
|
2456
2914
|
const metaEndian = meta?.endian;
|
2457
|
-
|
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
|
+
}
|
2458
2925
|
if (isIntegerType(type)) {
|
2459
2926
|
return this.readInteger(base, endian, stream);
|
2460
2927
|
}
|
@@ -2531,7 +2998,7 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2531
2998
|
}
|
2532
2999
|
/**
|
2533
3000
|
* Apply processing transformation to data.
|
2534
|
-
*
|
3001
|
+
* Delegates to the process utility module.
|
2535
3002
|
*
|
2536
3003
|
* @param data - Data to process
|
2537
3004
|
* @param process - Processing specification
|
@@ -2539,13 +3006,35 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2539
3006
|
* @private
|
2540
3007
|
*/
|
2541
3008
|
applyProcessing(data, process) {
|
2542
|
-
|
2543
|
-
|
2544
|
-
|
2545
|
-
|
2546
|
-
|
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;
|
2547
3036
|
}
|
2548
|
-
return
|
3037
|
+
return "le";
|
2549
3038
|
}
|
2550
3039
|
/**
|
2551
3040
|
* Evaluate a value that can be an expression or literal.
|
@@ -2687,6 +3176,12 @@ export {
|
|
2687
3176
|
* @author Fabiano Pinto
|
2688
3177
|
* @license MIT
|
2689
3178
|
*/
|
3179
|
+
/**
|
3180
|
+
* @fileoverview Data processing utilities for Kaitai Struct
|
3181
|
+
* @module utils/process
|
3182
|
+
* @author Fabiano Pinto
|
3183
|
+
* @license MIT
|
3184
|
+
*/
|
2690
3185
|
/**
|
2691
3186
|
* @fileoverview Type interpreter for executing Kaitai Struct schemas
|
2692
3187
|
* @module interpreter/TypeInterpreter
|