@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.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
|
}
|
@@ -1857,15 +1895,15 @@ var Evaluator = class {
|
|
1857
1895
|
return !this.equals(leftVal, rightVal);
|
1858
1896
|
// Bitwise
|
1859
1897
|
case "<<":
|
1860
|
-
return this.
|
1898
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a << b);
|
1861
1899
|
case ">>":
|
1862
|
-
return this.
|
1900
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a >> b);
|
1863
1901
|
case "&":
|
1864
|
-
return this.
|
1902
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a & b);
|
1865
1903
|
case "|":
|
1866
|
-
return this.
|
1904
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a | b);
|
1867
1905
|
case "^":
|
1868
|
-
return this.
|
1906
|
+
return this.bitwiseOp(leftVal, rightVal, (a, b) => a ^ b);
|
1869
1907
|
// Logical
|
1870
1908
|
case "and":
|
1871
1909
|
return this.toBoolean(leftVal) && this.toBoolean(rightVal);
|
@@ -1909,6 +1947,16 @@ var Evaluator = class {
|
|
1909
1947
|
`Cannot access property ${property} of null/undefined`
|
1910
1948
|
);
|
1911
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
|
+
}
|
1912
1960
|
if (typeof obj === "object") {
|
1913
1961
|
return obj[property];
|
1914
1962
|
}
|
@@ -1935,8 +1983,9 @@ var Evaluator = class {
|
|
1935
1983
|
* Evaluate method call (object.method()).
|
1936
1984
|
* @private
|
1937
1985
|
*/
|
1938
|
-
evaluateMethodCall(object, method,
|
1986
|
+
evaluateMethodCall(object, method, args, context) {
|
1939
1987
|
const obj = this.evaluate(object, context);
|
1988
|
+
const evalArgs = args.map((arg) => this.evaluate(arg, context));
|
1940
1989
|
if (method === "length" || method === "size") {
|
1941
1990
|
if (Array.isArray(obj)) return obj.length;
|
1942
1991
|
if (obj instanceof Uint8Array) return obj.length;
|
@@ -1944,13 +1993,182 @@ var Evaluator = class {
|
|
1944
1993
|
throw new ParseError(`Object does not have a ${method} property`);
|
1945
1994
|
}
|
1946
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
|
+
}
|
1947
2000
|
return this.toInt(obj);
|
1948
2001
|
}
|
1949
2002
|
if (method === "to_s") {
|
1950
2003
|
return String(obj);
|
1951
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
|
+
}
|
1952
2011
|
throw new ParseError(`Unknown method: ${method}`);
|
1953
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
|
+
}
|
1954
2172
|
/**
|
1955
2173
|
* Evaluate enum access (EnumName::value).
|
1956
2174
|
* @private
|
@@ -1980,6 +2198,38 @@ var Evaluator = class {
|
|
1980
2198
|
const result = a % b;
|
1981
2199
|
return result < 0 ? result + b : result;
|
1982
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
|
+
}
|
1983
2233
|
/**
|
1984
2234
|
* Helper: Compare two values.
|
1985
2235
|
* @private
|
@@ -2067,6 +2317,112 @@ function evaluateExpression(expression, context) {
|
|
2067
2317
|
return evaluator.evaluate(ast, context);
|
2068
2318
|
}
|
2069
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
|
+
|
2070
2426
|
// src/interpreter/TypeInterpreter.ts
|
2071
2427
|
var TypeInterpreter = class _TypeInterpreter {
|
2072
2428
|
/**
|
@@ -2103,13 +2459,20 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2103
2459
|
* @param stream - Binary stream to parse
|
2104
2460
|
* @param parent - Parent object (for nested types)
|
2105
2461
|
* @param typeArgs - Arguments for parametric types
|
2462
|
+
* @param root - Root object of the parse tree (for nested types)
|
2106
2463
|
* @returns Parsed object
|
2107
2464
|
*/
|
2108
|
-
parse(stream, parent, typeArgs) {
|
2465
|
+
parse(stream, parent, typeArgs, root) {
|
2109
2466
|
const result = {};
|
2110
|
-
const
|
2467
|
+
const actualRoot = root || result;
|
2468
|
+
const context = new Context(stream, actualRoot, parent, this.schema.enums);
|
2111
2469
|
context.current = result;
|
2470
|
+
const startPos = stream.pos;
|
2112
2471
|
result["_io"] = stream;
|
2472
|
+
if (root) {
|
2473
|
+
;
|
2474
|
+
result["_root"] = root;
|
2475
|
+
}
|
2113
2476
|
if (typeArgs && this.schema.params) {
|
2114
2477
|
for (let i = 0; i < this.schema.params.length && i < typeArgs.length; i++) {
|
2115
2478
|
const param = this.schema.params[i];
|
@@ -2118,6 +2481,9 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2118
2481
|
context.set(param.id, evaluatedArg);
|
2119
2482
|
}
|
2120
2483
|
}
|
2484
|
+
if (this.schema.instances) {
|
2485
|
+
this.setupInstances(result, stream, context);
|
2486
|
+
}
|
2121
2487
|
if (this.schema.seq) {
|
2122
2488
|
for (const attr of this.schema.seq) {
|
2123
2489
|
const value = this.parseAttribute(attr, context);
|
@@ -2126,9 +2492,8 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2126
2492
|
}
|
2127
2493
|
}
|
2128
2494
|
}
|
2129
|
-
|
2130
|
-
|
2131
|
-
}
|
2495
|
+
const endPos = stream.pos;
|
2496
|
+
result["_sizeof"] = endPos - startPos;
|
2132
2497
|
return result;
|
2133
2498
|
}
|
2134
2499
|
/**
|
@@ -2433,11 +2798,13 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2433
2798
|
context
|
2434
2799
|
);
|
2435
2800
|
}
|
2436
|
-
|
2437
|
-
|
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);
|
2438
2805
|
}
|
2439
|
-
if (this.schema.types &&
|
2440
|
-
const typeSchema = this.schema.types[
|
2806
|
+
if (this.schema.types && typeName in this.schema.types) {
|
2807
|
+
const typeSchema = this.schema.types[typeName];
|
2441
2808
|
const meta = this.schema.meta || this.parentMeta;
|
2442
2809
|
if (this.schema.enums && !typeSchema.enums) {
|
2443
2810
|
typeSchema.enums = this.schema.enums;
|
@@ -2446,9 +2813,100 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2446
2813
|
typeSchema.types = this.schema.types;
|
2447
2814
|
}
|
2448
2815
|
const interpreter = new _TypeInterpreter(typeSchema, meta);
|
2449
|
-
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;
|
2450
2909
|
}
|
2451
|
-
throw new ParseError(`Unknown type: ${type}`);
|
2452
2910
|
}
|
2453
2911
|
/**
|
2454
2912
|
* Parse a switch type (type selection based on expression).
|
@@ -2491,12 +2949,21 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2491
2949
|
* @returns Parsed value
|
2492
2950
|
* @private
|
2493
2951
|
*/
|
2494
|
-
parseBuiltinType(type, stream,
|
2952
|
+
parseBuiltinType(type, stream, context) {
|
2495
2953
|
const base = getBaseType(type);
|
2496
2954
|
const typeEndian = getTypeEndianness(type);
|
2497
2955
|
const meta = this.schema.meta || this.parentMeta;
|
2498
2956
|
const metaEndian = meta?.endian;
|
2499
|
-
|
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
|
+
}
|
2500
2967
|
if (isIntegerType(type)) {
|
2501
2968
|
return this.readInteger(base, endian, stream);
|
2502
2969
|
}
|
@@ -2573,7 +3040,7 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2573
3040
|
}
|
2574
3041
|
/**
|
2575
3042
|
* Apply processing transformation to data.
|
2576
|
-
*
|
3043
|
+
* Delegates to the process utility module.
|
2577
3044
|
*
|
2578
3045
|
* @param data - Data to process
|
2579
3046
|
* @param process - Processing specification
|
@@ -2581,13 +3048,35 @@ var TypeInterpreter = class _TypeInterpreter {
|
|
2581
3048
|
* @private
|
2582
3049
|
*/
|
2583
3050
|
applyProcessing(data, process) {
|
2584
|
-
|
2585
|
-
|
2586
|
-
|
2587
|
-
|
2588
|
-
|
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;
|
2589
3078
|
}
|
2590
|
-
return
|
3079
|
+
return "le";
|
2591
3080
|
}
|
2592
3081
|
/**
|
2593
3082
|
* Evaluate a value that can be an expression or literal.
|
@@ -2730,6 +3219,12 @@ function parse(ksyYaml, buffer, options = {}) {
|
|
2730
3219
|
* @author Fabiano Pinto
|
2731
3220
|
* @license MIT
|
2732
3221
|
*/
|
3222
|
+
/**
|
3223
|
+
* @fileoverview Data processing utilities for Kaitai Struct
|
3224
|
+
* @module utils/process
|
3225
|
+
* @author Fabiano Pinto
|
3226
|
+
* @license MIT
|
3227
|
+
*/
|
2733
3228
|
/**
|
2734
3229
|
* @fileoverview Type interpreter for executing Kaitai Struct schemas
|
2735
3230
|
* @module interpreter/TypeInterpreter
|