@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/cli.js CHANGED
@@ -75,41 +75,72 @@ var import_yaml = require("yaml");
75
75
 
76
76
  // src/utils/errors.ts
77
77
  var KaitaiError = class _KaitaiError extends Error {
78
- constructor(message, position) {
79
- super(message);
78
+ constructor(message, position, context) {
79
+ super(_KaitaiError.formatMessage(message, position, context));
80
80
  this.position = position;
81
+ this.context = context;
81
82
  this.name = "KaitaiError";
82
83
  Object.setPrototypeOf(this, _KaitaiError.prototype);
83
84
  }
85
+ /**
86
+ * Format error message with position and context.
87
+ * @private
88
+ */
89
+ static formatMessage(message, position, context) {
90
+ let formatted = message;
91
+ if (position !== void 0) {
92
+ formatted += ` (at byte offset 0x${position.toString(16).toUpperCase()})`;
93
+ }
94
+ if (context && context.length > 0) {
95
+ const hexContext = _KaitaiError.formatHexContext(context, position);
96
+ formatted += `
97
+ ${hexContext}`;
98
+ }
99
+ return formatted;
100
+ }
101
+ /**
102
+ * Format hex dump context around error position.
103
+ * @private
104
+ */
105
+ static formatHexContext(data, position) {
106
+ const contextSize = 16;
107
+ const start = Math.max(0, (position ?? 0) - contextSize);
108
+ const end = Math.min(data.length, (position ?? 0) + contextSize);
109
+ const chunk = data.slice(start, end);
110
+ const lines = ["Context:"];
111
+ let offset = start;
112
+ for (let i = 0; i < chunk.length; i += 16) {
113
+ const lineBytes = chunk.slice(i, i + 16);
114
+ const hex = Array.from(lineBytes).map((b) => b.toString(16).padStart(2, "0")).join(" ");
115
+ const ascii = Array.from(lineBytes).map((b) => b >= 32 && b <= 126 ? String.fromCharCode(b) : ".").join("");
116
+ const offsetStr = ` ${(offset + i).toString(16).padStart(8, "0")}`;
117
+ const marker = position !== void 0 && position >= offset + i && position < offset + i + lineBytes.length ? " <--" : "";
118
+ lines.push(`${offsetStr}: ${hex.padEnd(48, " ")} | ${ascii}${marker}`);
119
+ }
120
+ return lines.join("\n");
121
+ }
84
122
  };
85
123
  var ValidationError = class _ValidationError extends KaitaiError {
86
- constructor(message, position) {
87
- super(message, position);
124
+ constructor(message, position, context) {
125
+ super(message, position, context);
88
126
  this.name = "ValidationError";
89
127
  Object.setPrototypeOf(this, _ValidationError.prototype);
90
128
  }
91
129
  };
92
130
  var ParseError = class _ParseError extends KaitaiError {
93
- constructor(message, position) {
94
- super(message, position);
131
+ constructor(message, position, context) {
132
+ super(message, position, context);
95
133
  this.name = "ParseError";
96
134
  Object.setPrototypeOf(this, _ParseError.prototype);
97
135
  }
98
136
  };
99
137
  var EOFError = class _EOFError extends KaitaiError {
100
- constructor(message = "Unexpected end of stream", position) {
101
- super(message, position);
138
+ constructor(message = "Unexpected end of stream", position, context) {
139
+ super(message, position, context);
102
140
  this.name = "EOFError";
103
141
  Object.setPrototypeOf(this, _EOFError.prototype);
104
142
  }
105
143
  };
106
- var NotImplementedError = class _NotImplementedError extends KaitaiError {
107
- constructor(feature) {
108
- super(`Feature not yet implemented: ${feature}`);
109
- this.name = "NotImplementedError";
110
- Object.setPrototypeOf(this, _NotImplementedError.prototype);
111
- }
112
- };
113
144
 
114
145
  // src/parser/KsyParser.ts
115
146
  var KsyParser = class {
@@ -1826,15 +1857,15 @@ var Evaluator = class {
1826
1857
  return !this.equals(leftVal, rightVal);
1827
1858
  // Bitwise
1828
1859
  case "<<":
1829
- return this.toInt(leftVal) << this.toInt(rightVal);
1860
+ return this.bitwiseOp(leftVal, rightVal, (a, b) => a << b);
1830
1861
  case ">>":
1831
- return this.toInt(leftVal) >> this.toInt(rightVal);
1862
+ return this.bitwiseOp(leftVal, rightVal, (a, b) => a >> b);
1832
1863
  case "&":
1833
- return this.toInt(leftVal) & this.toInt(rightVal);
1864
+ return this.bitwiseOp(leftVal, rightVal, (a, b) => a & b);
1834
1865
  case "|":
1835
- return this.toInt(leftVal) | this.toInt(rightVal);
1866
+ return this.bitwiseOp(leftVal, rightVal, (a, b) => a | b);
1836
1867
  case "^":
1837
- return this.toInt(leftVal) ^ this.toInt(rightVal);
1868
+ return this.bitwiseOp(leftVal, rightVal, (a, b) => a ^ b);
1838
1869
  // Logical
1839
1870
  case "and":
1840
1871
  return this.toBoolean(leftVal) && this.toBoolean(rightVal);
@@ -1878,6 +1909,16 @@ var Evaluator = class {
1878
1909
  `Cannot access property ${property} of null/undefined`
1879
1910
  );
1880
1911
  }
1912
+ if (property === "to_i") {
1913
+ if (typeof obj === "number") return Math.floor(obj);
1914
+ if (typeof obj === "bigint") return Number(obj);
1915
+ if (typeof obj === "string") return parseInt(obj, 10);
1916
+ if (typeof obj === "boolean") return obj ? 1 : 0;
1917
+ return this.toInt(obj);
1918
+ }
1919
+ if (property === "to_s") {
1920
+ return String(obj);
1921
+ }
1881
1922
  if (typeof obj === "object") {
1882
1923
  return obj[property];
1883
1924
  }
@@ -1904,8 +1945,9 @@ var Evaluator = class {
1904
1945
  * Evaluate method call (object.method()).
1905
1946
  * @private
1906
1947
  */
1907
- evaluateMethodCall(object, method, _args, context) {
1948
+ evaluateMethodCall(object, method, args, context) {
1908
1949
  const obj = this.evaluate(object, context);
1950
+ const evalArgs = args.map((arg) => this.evaluate(arg, context));
1909
1951
  if (method === "length" || method === "size") {
1910
1952
  if (Array.isArray(obj)) return obj.length;
1911
1953
  if (obj instanceof Uint8Array) return obj.length;
@@ -1913,13 +1955,182 @@ var Evaluator = class {
1913
1955
  throw new ParseError(`Object does not have a ${method} property`);
1914
1956
  }
1915
1957
  if (method === "to_i") {
1958
+ const base = evalArgs.length > 0 ? this.toInt(evalArgs[0]) : 10;
1959
+ if (typeof obj === "string") {
1960
+ return parseInt(obj, base);
1961
+ }
1916
1962
  return this.toInt(obj);
1917
1963
  }
1918
1964
  if (method === "to_s") {
1919
1965
  return String(obj);
1920
1966
  }
1967
+ if (typeof obj === "string") {
1968
+ return this.evaluateStringMethod(obj, method, evalArgs);
1969
+ }
1970
+ if (Array.isArray(obj) || obj instanceof Uint8Array) {
1971
+ return this.evaluateArrayMethod(obj, method, evalArgs);
1972
+ }
1921
1973
  throw new ParseError(`Unknown method: ${method}`);
1922
1974
  }
1975
+ /**
1976
+ * Evaluate string methods.
1977
+ * @private
1978
+ */
1979
+ evaluateStringMethod(str, method, args) {
1980
+ switch (method) {
1981
+ case "substring": {
1982
+ const start = args.length > 0 ? this.toInt(args[0]) : 0;
1983
+ const end = args.length > 1 ? this.toInt(args[1]) : void 0;
1984
+ return str.substring(start, end);
1985
+ }
1986
+ case "substr": {
1987
+ const start = args.length > 0 ? this.toInt(args[0]) : 0;
1988
+ const length = args.length > 1 ? this.toInt(args[1]) : void 0;
1989
+ return str.substr(start, length);
1990
+ }
1991
+ case "reverse":
1992
+ return str.split("").reverse().join("");
1993
+ case "to_i": {
1994
+ const base = args.length > 0 ? this.toInt(args[0]) : 10;
1995
+ return parseInt(str, base);
1996
+ }
1997
+ case "length":
1998
+ case "size":
1999
+ return str.length;
2000
+ // Ruby-style string methods used in Kaitai
2001
+ case "upcase":
2002
+ case "to_upper":
2003
+ return str.toUpperCase();
2004
+ case "downcase":
2005
+ case "to_lower":
2006
+ return str.toLowerCase();
2007
+ case "capitalize":
2008
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
2009
+ case "strip":
2010
+ case "trim":
2011
+ return str.trim();
2012
+ case "lstrip":
2013
+ case "trim_start":
2014
+ return str.trimStart();
2015
+ case "rstrip":
2016
+ case "trim_end":
2017
+ return str.trimEnd();
2018
+ case "starts_with":
2019
+ case "startsWith": {
2020
+ if (args.length === 0) {
2021
+ throw new ParseError("starts_with requires 1 argument");
2022
+ }
2023
+ return str.startsWith(String(args[0]));
2024
+ }
2025
+ case "ends_with":
2026
+ case "endsWith": {
2027
+ if (args.length === 0) {
2028
+ throw new ParseError("ends_with requires 1 argument");
2029
+ }
2030
+ return str.endsWith(String(args[0]));
2031
+ }
2032
+ case "includes":
2033
+ case "contains": {
2034
+ if (args.length === 0) {
2035
+ throw new ParseError("includes requires 1 argument");
2036
+ }
2037
+ return str.includes(String(args[0]));
2038
+ }
2039
+ case "index_of":
2040
+ case "indexOf": {
2041
+ if (args.length === 0) {
2042
+ throw new ParseError("index_of requires 1 argument");
2043
+ }
2044
+ return str.indexOf(String(args[0]));
2045
+ }
2046
+ case "split": {
2047
+ if (args.length === 0) {
2048
+ throw new ParseError("split requires 1 argument");
2049
+ }
2050
+ return str.split(String(args[0]));
2051
+ }
2052
+ case "replace": {
2053
+ if (args.length < 2) {
2054
+ throw new ParseError("replace requires 2 arguments");
2055
+ }
2056
+ return str.replace(String(args[0]), String(args[1]));
2057
+ }
2058
+ case "replace_all":
2059
+ case "replaceAll": {
2060
+ if (args.length < 2) {
2061
+ throw new ParseError("replace_all requires 2 arguments");
2062
+ }
2063
+ const search = String(args[0]);
2064
+ const replace = String(args[1]);
2065
+ return str.split(search).join(replace);
2066
+ }
2067
+ case "pad_left":
2068
+ case "padStart": {
2069
+ if (args.length === 0) {
2070
+ throw new ParseError("pad_left requires at least 1 argument");
2071
+ }
2072
+ const length = this.toInt(args[0]);
2073
+ const fillString = args.length > 1 ? String(args[1]) : " ";
2074
+ return str.padStart(length, fillString);
2075
+ }
2076
+ case "pad_right":
2077
+ case "padEnd": {
2078
+ if (args.length === 0) {
2079
+ throw new ParseError("pad_right requires at least 1 argument");
2080
+ }
2081
+ const length = this.toInt(args[0]);
2082
+ const fillString = args.length > 1 ? String(args[1]) : " ";
2083
+ return str.padEnd(length, fillString);
2084
+ }
2085
+ default:
2086
+ throw new ParseError(`Unknown string method: ${method}`);
2087
+ }
2088
+ }
2089
+ /**
2090
+ * Evaluate array methods.
2091
+ * @private
2092
+ */
2093
+ evaluateArrayMethod(arr, method, args) {
2094
+ const array = Array.isArray(arr) ? arr : Array.from(arr);
2095
+ switch (method) {
2096
+ case "length":
2097
+ case "size":
2098
+ return array.length;
2099
+ case "first":
2100
+ return array[0];
2101
+ case "last":
2102
+ return array[array.length - 1];
2103
+ case "min":
2104
+ return Math.min(...array.map((v) => this.toNumber(v)));
2105
+ case "max":
2106
+ return Math.max(...array.map((v) => this.toNumber(v)));
2107
+ case "reverse":
2108
+ return [...array].reverse();
2109
+ case "sort":
2110
+ return [...array].sort((a, b) => this.compare(a, b));
2111
+ case "includes":
2112
+ case "contains": {
2113
+ if (args.length === 0) {
2114
+ throw new ParseError("includes requires 1 argument");
2115
+ }
2116
+ return array.some((item) => this.equals(item, args[0]));
2117
+ }
2118
+ case "index_of":
2119
+ case "indexOf": {
2120
+ if (args.length === 0) {
2121
+ throw new ParseError("index_of requires 1 argument");
2122
+ }
2123
+ return array.findIndex((item) => this.equals(item, args[0]));
2124
+ }
2125
+ case "slice": {
2126
+ const start = args.length > 0 ? this.toInt(args[0]) : 0;
2127
+ const end = args.length > 1 ? this.toInt(args[1]) : void 0;
2128
+ return array.slice(start, end);
2129
+ }
2130
+ default:
2131
+ throw new ParseError(`Unknown array method: ${method}`);
2132
+ }
2133
+ }
1923
2134
  /**
1924
2135
  * Evaluate enum access (EnumName::value).
1925
2136
  * @private
@@ -1949,6 +2160,38 @@ var Evaluator = class {
1949
2160
  const result = a % b;
1950
2161
  return result < 0 ? result + b : result;
1951
2162
  }
2163
+ /**
2164
+ * Helper: Bitwise operation with BigInt support.
2165
+ * JavaScript bitwise operators work on 32-bit integers, but Kaitai
2166
+ * may use 64-bit values. For values that fit in 32 bits, use native ops.
2167
+ * For larger values, convert to BigInt (with limitations).
2168
+ * @private
2169
+ */
2170
+ bitwiseOp(left, right, op) {
2171
+ if (typeof left === "bigint" || typeof right === "bigint") {
2172
+ const leftBig = typeof left === "bigint" ? left : BigInt(left);
2173
+ const rightBig = typeof right === "bigint" ? right : BigInt(right);
2174
+ if (op.toString().includes("<<")) {
2175
+ return leftBig << BigInt(Number(rightBig));
2176
+ }
2177
+ if (op.toString().includes(">>")) {
2178
+ return leftBig >> BigInt(Number(rightBig));
2179
+ }
2180
+ if (op.toString().includes("&")) {
2181
+ return leftBig & rightBig;
2182
+ }
2183
+ if (op.toString().includes("|")) {
2184
+ return leftBig | rightBig;
2185
+ }
2186
+ if (op.toString().includes("^")) {
2187
+ return leftBig ^ rightBig;
2188
+ }
2189
+ }
2190
+ if (left === void 0 || left === null || right === void 0 || right === null) {
2191
+ throw new ParseError("Cannot perform bitwise operation on null/undefined");
2192
+ }
2193
+ return op(this.toInt(left), this.toInt(right));
2194
+ }
1952
2195
  /**
1953
2196
  * Helper: Compare two values.
1954
2197
  * @private
@@ -2036,6 +2279,112 @@ function evaluateExpression(expression, context) {
2036
2279
  return evaluator.evaluate(ast, context);
2037
2280
  }
2038
2281
 
2282
+ // src/utils/process.ts
2283
+ var import_pako = require("pako");
2284
+ function applyProcess(data, process2) {
2285
+ const spec = typeof process2 === "string" ? { algorithm: process2 } : process2;
2286
+ const algorithm = spec.algorithm;
2287
+ if (!algorithm) {
2288
+ throw new ParseError("Process specification missing algorithm");
2289
+ }
2290
+ switch (algorithm) {
2291
+ case "zlib":
2292
+ return processZlib(data);
2293
+ case "xor":
2294
+ return processXor(data, spec.key);
2295
+ case "rol":
2296
+ return processRol(data, spec.amount, spec.group);
2297
+ case "ror":
2298
+ return processRor(data, spec.amount, spec.group);
2299
+ case "bswap2":
2300
+ return processByteswap(data, 2);
2301
+ case "bswap4":
2302
+ return processByteswap(data, 4);
2303
+ case "bswap8":
2304
+ return processByteswap(data, 8);
2305
+ case "bswap16":
2306
+ return processByteswap(data, 16);
2307
+ default:
2308
+ throw new ParseError(
2309
+ `Unknown process algorithm: ${algorithm}. Supported: zlib, xor, rol, ror, bswap2, bswap4, bswap8, bswap16`
2310
+ );
2311
+ }
2312
+ }
2313
+ function processZlib(data) {
2314
+ try {
2315
+ return (0, import_pako.inflate)(data);
2316
+ } catch (error) {
2317
+ throw new ParseError(
2318
+ `Zlib decompression failed: ${error instanceof Error ? error.message : String(error)}`
2319
+ );
2320
+ }
2321
+ }
2322
+ function processXor(data, key) {
2323
+ if (key === void 0) {
2324
+ throw new ParseError("XOR process requires a key parameter");
2325
+ }
2326
+ const result = new Uint8Array(data.length);
2327
+ const keyBytes = Array.isArray(key) ? key : [key];
2328
+ if (keyBytes.length === 0) {
2329
+ throw new ParseError("XOR key cannot be empty");
2330
+ }
2331
+ for (let i = 0; i < data.length; i++) {
2332
+ result[i] = data[i] ^ keyBytes[i % keyBytes.length];
2333
+ }
2334
+ return result;
2335
+ }
2336
+ function processRol(data, amount, group) {
2337
+ const bits = amount ?? 1;
2338
+ const groupSize = group ?? 1;
2339
+ if (bits < 0 || bits > 7) {
2340
+ throw new ParseError("ROL amount must be between 0 and 7");
2341
+ }
2342
+ if (groupSize !== 1) {
2343
+ throw new ParseError("ROL with group size > 1 not yet supported");
2344
+ }
2345
+ const result = new Uint8Array(data.length);
2346
+ for (let i = 0; i < data.length; i++) {
2347
+ const byte = data[i];
2348
+ result[i] = (byte << bits | byte >> 8 - bits) & 255;
2349
+ }
2350
+ return result;
2351
+ }
2352
+ function processRor(data, amount, group) {
2353
+ const bits = amount ?? 1;
2354
+ const groupSize = group ?? 1;
2355
+ if (bits < 0 || bits > 7) {
2356
+ throw new ParseError("ROR amount must be between 0 and 7");
2357
+ }
2358
+ if (groupSize !== 1) {
2359
+ throw new ParseError("ROR with group size > 1 not yet supported");
2360
+ }
2361
+ const result = new Uint8Array(data.length);
2362
+ for (let i = 0; i < data.length; i++) {
2363
+ const byte = data[i];
2364
+ result[i] = (byte >> bits | byte << 8 - bits) & 255;
2365
+ }
2366
+ return result;
2367
+ }
2368
+ function processByteswap(data, groupSize) {
2369
+ if (![2, 4, 8, 16].includes(groupSize)) {
2370
+ throw new ParseError(
2371
+ `Invalid byteswap group size: ${groupSize}. Must be 2, 4, 8, or 16`
2372
+ );
2373
+ }
2374
+ if (data.length % groupSize !== 0) {
2375
+ throw new ParseError(
2376
+ `Data length ${data.length} is not aligned to group size ${groupSize}`
2377
+ );
2378
+ }
2379
+ const result = new Uint8Array(data.length);
2380
+ for (let i = 0; i < data.length; i += groupSize) {
2381
+ for (let j = 0; j < groupSize; j++) {
2382
+ result[i + j] = data[i + groupSize - 1 - j];
2383
+ }
2384
+ }
2385
+ return result;
2386
+ }
2387
+
2039
2388
  // src/interpreter/TypeInterpreter.ts
2040
2389
  var TypeInterpreter = class _TypeInterpreter {
2041
2390
  /**
@@ -2072,13 +2421,20 @@ var TypeInterpreter = class _TypeInterpreter {
2072
2421
  * @param stream - Binary stream to parse
2073
2422
  * @param parent - Parent object (for nested types)
2074
2423
  * @param typeArgs - Arguments for parametric types
2424
+ * @param root - Root object of the parse tree (for nested types)
2075
2425
  * @returns Parsed object
2076
2426
  */
2077
- parse(stream, parent, typeArgs) {
2427
+ parse(stream, parent, typeArgs, root) {
2078
2428
  const result = {};
2079
- const context = new Context(stream, result, parent, this.schema.enums);
2429
+ const actualRoot = root || result;
2430
+ const context = new Context(stream, actualRoot, parent, this.schema.enums);
2080
2431
  context.current = result;
2432
+ const startPos = stream.pos;
2081
2433
  result["_io"] = stream;
2434
+ if (root) {
2435
+ ;
2436
+ result["_root"] = root;
2437
+ }
2082
2438
  if (typeArgs && this.schema.params) {
2083
2439
  for (let i = 0; i < this.schema.params.length && i < typeArgs.length; i++) {
2084
2440
  const param = this.schema.params[i];
@@ -2087,6 +2443,9 @@ var TypeInterpreter = class _TypeInterpreter {
2087
2443
  context.set(param.id, evaluatedArg);
2088
2444
  }
2089
2445
  }
2446
+ if (this.schema.instances) {
2447
+ this.setupInstances(result, stream, context);
2448
+ }
2090
2449
  if (this.schema.seq) {
2091
2450
  for (const attr of this.schema.seq) {
2092
2451
  const value = this.parseAttribute(attr, context);
@@ -2095,9 +2454,8 @@ var TypeInterpreter = class _TypeInterpreter {
2095
2454
  }
2096
2455
  }
2097
2456
  }
2098
- if (this.schema.instances) {
2099
- this.setupInstances(result, stream, context);
2100
- }
2457
+ const endPos = stream.pos;
2458
+ result["_sizeof"] = endPos - startPos;
2101
2459
  return result;
2102
2460
  }
2103
2461
  /**
@@ -2402,11 +2760,13 @@ var TypeInterpreter = class _TypeInterpreter {
2402
2760
  context
2403
2761
  );
2404
2762
  }
2405
- if (isBuiltinType(type)) {
2406
- return this.parseBuiltinType(type, stream, context);
2763
+ const { typeName, args } = this.parseParameterizedType(type, context);
2764
+ const effectiveArgs = args.length > 0 ? args : typeArgs;
2765
+ if (isBuiltinType(typeName)) {
2766
+ return this.parseBuiltinType(typeName, stream, context);
2407
2767
  }
2408
- if (this.schema.types && type in this.schema.types) {
2409
- const typeSchema = this.schema.types[type];
2768
+ if (this.schema.types && typeName in this.schema.types) {
2769
+ const typeSchema = this.schema.types[typeName];
2410
2770
  const meta = this.schema.meta || this.parentMeta;
2411
2771
  if (this.schema.enums && !typeSchema.enums) {
2412
2772
  typeSchema.enums = this.schema.enums;
@@ -2415,9 +2775,100 @@ var TypeInterpreter = class _TypeInterpreter {
2415
2775
  typeSchema.types = this.schema.types;
2416
2776
  }
2417
2777
  const interpreter = new _TypeInterpreter(typeSchema, meta);
2418
- return interpreter.parse(stream, context.current, typeArgs);
2778
+ return interpreter.parse(
2779
+ stream,
2780
+ context.current,
2781
+ effectiveArgs,
2782
+ context.root
2783
+ );
2784
+ }
2785
+ throw new ParseError(`Unknown type: ${typeName}`);
2786
+ }
2787
+ /**
2788
+ * Parse parameterized type syntax and extract type name and arguments.
2789
+ * Supports: type_name(arg1, arg2, ...) or just type_name
2790
+ *
2791
+ * @param typeSpec - Type specification string
2792
+ * @param context - Execution context for evaluating argument expressions
2793
+ * @returns Object with typeName and evaluated args
2794
+ * @private
2795
+ */
2796
+ parseParameterizedType(typeSpec, context) {
2797
+ const match = typeSpec.match(/^([a-z_][a-z0-9_]*)\((.*)\)$/i);
2798
+ if (!match) {
2799
+ return { typeName: typeSpec, args: [] };
2800
+ }
2801
+ const typeName = match[1];
2802
+ const argsString = match[2].trim();
2803
+ if (!argsString) {
2804
+ return { typeName, args: [] };
2805
+ }
2806
+ const args = [];
2807
+ let current = "";
2808
+ let inString = false;
2809
+ let stringChar = "";
2810
+ let parenDepth = 0;
2811
+ for (let i = 0; i < argsString.length; i++) {
2812
+ const char = argsString[i];
2813
+ if (inString) {
2814
+ current += char;
2815
+ if (char === stringChar && argsString[i - 1] !== "\\") {
2816
+ inString = false;
2817
+ }
2818
+ } else if (char === '"' || char === "'") {
2819
+ inString = true;
2820
+ stringChar = char;
2821
+ current += char;
2822
+ } else if (char === "(") {
2823
+ parenDepth++;
2824
+ current += char;
2825
+ } else if (char === ")") {
2826
+ parenDepth--;
2827
+ current += char;
2828
+ } else if (char === "," && parenDepth === 0) {
2829
+ args.push(this.parseArgument(current.trim(), context));
2830
+ current = "";
2831
+ } else {
2832
+ current += char;
2833
+ }
2834
+ }
2835
+ if (current.trim()) {
2836
+ args.push(this.parseArgument(current.trim(), context));
2837
+ }
2838
+ return { typeName, args };
2839
+ }
2840
+ /**
2841
+ * Parse and evaluate a single type argument.
2842
+ *
2843
+ * @param arg - Argument string
2844
+ * @param context - Execution context
2845
+ * @returns Evaluated argument value
2846
+ * @private
2847
+ */
2848
+ parseArgument(arg, context) {
2849
+ if (arg === "true") return true;
2850
+ if (arg === "false") return false;
2851
+ if (arg.startsWith('"') && arg.endsWith('"') || arg.startsWith("'") && arg.endsWith("'")) {
2852
+ return arg.slice(1, -1);
2853
+ }
2854
+ if (/^-?\d+$/.test(arg)) {
2855
+ return parseInt(arg, 10);
2856
+ }
2857
+ if (/^-?\d+\.\d+$/.test(arg)) {
2858
+ return parseFloat(arg);
2859
+ }
2860
+ if (/^0x[0-9a-f]+$/i.test(arg)) {
2861
+ return parseInt(arg, 16);
2862
+ }
2863
+ try {
2864
+ const result = this.evaluateValue(arg, context);
2865
+ if (typeof result === "string" || typeof result === "number" || typeof result === "boolean") {
2866
+ return result;
2867
+ }
2868
+ return Number(result);
2869
+ } catch {
2870
+ return arg;
2419
2871
  }
2420
- throw new ParseError(`Unknown type: ${type}`);
2421
2872
  }
2422
2873
  /**
2423
2874
  * Parse a switch type (type selection based on expression).
@@ -2460,12 +2911,21 @@ var TypeInterpreter = class _TypeInterpreter {
2460
2911
  * @returns Parsed value
2461
2912
  * @private
2462
2913
  */
2463
- parseBuiltinType(type, stream, _context) {
2914
+ parseBuiltinType(type, stream, context) {
2464
2915
  const base = getBaseType(type);
2465
2916
  const typeEndian = getTypeEndianness(type);
2466
2917
  const meta = this.schema.meta || this.parentMeta;
2467
2918
  const metaEndian = meta?.endian;
2468
- const endian = typeEndian || (typeof metaEndian === "string" ? metaEndian : "le");
2919
+ let endian;
2920
+ if (typeEndian) {
2921
+ endian = typeEndian;
2922
+ } else if (typeof metaEndian === "string") {
2923
+ endian = metaEndian;
2924
+ } else if (metaEndian && typeof metaEndian === "object") {
2925
+ endian = this.evaluateEndianExpression(metaEndian, context);
2926
+ } else {
2927
+ endian = "le";
2928
+ }
2469
2929
  if (isIntegerType(type)) {
2470
2930
  return this.readInteger(base, endian, stream);
2471
2931
  }
@@ -2542,7 +3002,7 @@ var TypeInterpreter = class _TypeInterpreter {
2542
3002
  }
2543
3003
  /**
2544
3004
  * Apply processing transformation to data.
2545
- * Supports basic transformations like zlib decompression.
3005
+ * Delegates to the process utility module.
2546
3006
  *
2547
3007
  * @param data - Data to process
2548
3008
  * @param process - Processing specification
@@ -2550,13 +3010,35 @@ var TypeInterpreter = class _TypeInterpreter {
2550
3010
  * @private
2551
3011
  */
2552
3012
  applyProcessing(data, process2) {
2553
- const processType = typeof process2 === "string" ? process2 : process2.algorithm;
2554
- if (processType) {
2555
- throw new NotImplementedError(
2556
- `Processing type "${processType}" is not yet implemented. Supported in future versions with zlib, encryption, etc.`
2557
- );
3013
+ return applyProcess(data, process2);
3014
+ }
3015
+ /**
3016
+ * Evaluate expression-based endianness (switch-on).
3017
+ *
3018
+ * @param endianExpr - Endianness expression with switch-on and cases
3019
+ * @param context - Execution context
3020
+ * @returns Resolved endianness ('le' or 'be')
3021
+ * @private
3022
+ */
3023
+ evaluateEndianExpression(endianExpr, context) {
3024
+ const switchOn = endianExpr["switch-on"];
3025
+ const cases = endianExpr.cases;
3026
+ if (!switchOn || typeof switchOn !== "string") {
3027
+ throw new ParseError('Endian expression missing "switch-on" field');
3028
+ }
3029
+ if (!cases || typeof cases !== "object") {
3030
+ throw new ParseError('Endian expression missing "cases" field');
2558
3031
  }
2559
- return data;
3032
+ const switchValue = this.evaluateValue(switchOn, context);
3033
+ const key = String(switchValue);
3034
+ if (key in cases) {
3035
+ const endian = cases[key];
3036
+ if (endian !== "le" && endian !== "be") {
3037
+ throw new ParseError(`Invalid endianness value: ${endian}`);
3038
+ }
3039
+ return endian;
3040
+ }
3041
+ return "le";
2560
3042
  }
2561
3043
  /**
2562
3044
  * Evaluate a value that can be an expression or literal.
@@ -2755,17 +3237,44 @@ function extractField(obj, path) {
2755
3237
  }
2756
3238
  return current;
2757
3239
  }
2758
- function jsonReplacer(_key, value) {
2759
- if (typeof value === "bigint") {
2760
- return value.toString();
3240
+ function safeStringify(data, pretty) {
3241
+ function safeClone(obj, seen = /* @__PURE__ */ new WeakSet()) {
3242
+ if (obj === null || obj === void 0) return obj;
3243
+ if (typeof obj === "bigint") return String(obj);
3244
+ if (typeof obj === "string" || typeof obj === "number" || typeof obj === "boolean")
3245
+ return obj;
3246
+ if (obj instanceof Uint8Array) return Array.from(obj);
3247
+ if (typeof obj === "object") {
3248
+ if (seen.has(obj)) return "[Circular]";
3249
+ seen.add(obj);
3250
+ }
3251
+ if (Array.isArray(obj)) {
3252
+ return obj.map((item) => safeClone(item, seen));
3253
+ }
3254
+ if (typeof obj === "object") {
3255
+ const result = {};
3256
+ const objRecord = obj;
3257
+ for (const key in objRecord) {
3258
+ if (key === "_io" || key === "_root" || key === "_parent") continue;
3259
+ try {
3260
+ const value = objRecord[key];
3261
+ result[key] = safeClone(value, seen);
3262
+ } catch (error) {
3263
+ result[key] = `[Error: ${error instanceof Error ? error.message : "unavailable"}]`;
3264
+ }
3265
+ }
3266
+ return result;
3267
+ }
3268
+ return obj;
2761
3269
  }
2762
- return value;
3270
+ const safe = safeClone(data);
3271
+ return pretty ? JSON.stringify(safe, null, 2) : JSON.stringify(safe);
2763
3272
  }
2764
3273
  function formatOutput(data, format, pretty) {
2765
3274
  if (format === "yaml") {
2766
- return JSON.stringify(data, jsonReplacer, 2).replace(/^{$/gm, "").replace(/^}$/gm, "").replace(/^\s*"([^"]+)":\s*/gm, "$1: ").replace(/,$/gm, "");
3275
+ return safeStringify(data, true).replace(/^{$/gm, "").replace(/^}$/gm, "").replace(/^\s*"([^"]+)":\s*/gm, "$1: ").replace(/,$/gm, "");
2767
3276
  }
2768
- return pretty ? JSON.stringify(data, jsonReplacer, 2) : JSON.stringify(data, jsonReplacer);
3277
+ return safeStringify(data, pretty);
2769
3278
  }
2770
3279
  function main() {
2771
3280
  const { options, positional } = parseCliArgs();
@@ -2912,12 +3421,6 @@ main();
2912
3421
  * @author Fabiano Pinto
2913
3422
  * @license MIT
2914
3423
  */
2915
- /**
2916
- * @fileoverview Binary stream reading functionality
2917
- * @module stream
2918
- * @author Fabiano Pinto
2919
- * @license MIT
2920
- */
2921
3424
  /**
2922
3425
  * @fileoverview Token types for Kaitai Struct expression language
2923
3426
  * @module expression/Token
@@ -2954,6 +3457,12 @@ main();
2954
3457
  * @author Fabiano Pinto
2955
3458
  * @license MIT
2956
3459
  */
3460
+ /**
3461
+ * @fileoverview Data processing utilities for Kaitai Struct
3462
+ * @module utils/process
3463
+ * @author Fabiano Pinto
3464
+ * @license MIT
3465
+ */
2957
3466
  /**
2958
3467
  * @fileoverview Type interpreter for executing Kaitai Struct schemas
2959
3468
  * @module interpreter/TypeInterpreter
@@ -2966,6 +3475,12 @@ main();
2966
3475
  * @author Fabiano Pinto
2967
3476
  * @license MIT
2968
3477
  */
3478
+ /**
3479
+ * @fileoverview Binary stream reading functionality
3480
+ * @module stream
3481
+ * @author Fabiano Pinto
3482
+ * @license MIT
3483
+ */
2969
3484
  /**
2970
3485
  * @fileoverview CLI utility for parsing binary files with Kaitai Struct definitions
2971
3486
  * @module kaitai-struct-ts/cli