swf-toolkit 0.0.0-stage → 0.1.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.
Files changed (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -2
  3. package/README.tr.md +126 -0
  4. package/dist/abc/abc-file.d.ts +168 -0
  5. package/dist/abc/abc-file.js +656 -0
  6. package/dist/abc/analysis.d.ts +36 -0
  7. package/dist/abc/analysis.js +213 -0
  8. package/dist/abc/code.d.ts +59 -0
  9. package/dist/abc/code.js +379 -0
  10. package/dist/abc/constants.d.ts +80 -0
  11. package/dist/abc/constants.js +93 -0
  12. package/dist/abc/interner.d.ts +46 -0
  13. package/dist/abc/interner.js +168 -0
  14. package/dist/abc/model.d.ts +40 -0
  15. package/dist/abc/model.js +146 -0
  16. package/dist/abc/opcodes.d.ts +39 -0
  17. package/dist/abc/opcodes.js +196 -0
  18. package/dist/abc/pcode/assembler.d.ts +42 -0
  19. package/dist/abc/pcode/assembler.js +635 -0
  20. package/dist/abc/pcode/disassembler.d.ts +17 -0
  21. package/dist/abc/pcode/disassembler.js +147 -0
  22. package/dist/abc/pcode/format.d.ts +23 -0
  23. package/dist/abc/pcode/format.js +131 -0
  24. package/dist/abc/pcode/lexer.d.ts +34 -0
  25. package/dist/abc/pcode/lexer.js +183 -0
  26. package/dist/assets/assets.d.ts +26 -0
  27. package/dist/assets/assets.js +151 -0
  28. package/dist/assets/images.d.ts +31 -0
  29. package/dist/assets/images.js +238 -0
  30. package/dist/assets/png.d.ts +12 -0
  31. package/dist/assets/png.js +196 -0
  32. package/dist/assets/sounds.d.ts +37 -0
  33. package/dist/assets/sounds.js +236 -0
  34. package/dist/avm1/avm1.d.ts +55 -0
  35. package/dist/avm1/avm1.js +642 -0
  36. package/dist/avm1/project.d.ts +16 -0
  37. package/dist/avm1/project.js +81 -0
  38. package/dist/cjs/abc/abc-file.d.ts +168 -0
  39. package/dist/cjs/abc/abc-file.js +663 -0
  40. package/dist/cjs/abc/analysis.d.ts +36 -0
  41. package/dist/cjs/abc/analysis.js +221 -0
  42. package/dist/cjs/abc/code.d.ts +59 -0
  43. package/dist/cjs/abc/code.js +387 -0
  44. package/dist/cjs/abc/constants.d.ts +80 -0
  45. package/dist/cjs/abc/constants.js +97 -0
  46. package/dist/cjs/abc/interner.d.ts +46 -0
  47. package/dist/cjs/abc/interner.js +172 -0
  48. package/dist/cjs/abc/model.d.ts +40 -0
  49. package/dist/cjs/abc/model.js +155 -0
  50. package/dist/cjs/abc/opcodes.d.ts +39 -0
  51. package/dist/cjs/abc/opcodes.js +201 -0
  52. package/dist/cjs/abc/pcode/assembler.d.ts +42 -0
  53. package/dist/cjs/abc/pcode/assembler.js +640 -0
  54. package/dist/cjs/abc/pcode/disassembler.d.ts +17 -0
  55. package/dist/cjs/abc/pcode/disassembler.js +152 -0
  56. package/dist/cjs/abc/pcode/format.d.ts +23 -0
  57. package/dist/cjs/abc/pcode/format.js +141 -0
  58. package/dist/cjs/abc/pcode/lexer.d.ts +34 -0
  59. package/dist/cjs/abc/pcode/lexer.js +188 -0
  60. package/dist/cjs/assets/assets.d.ts +26 -0
  61. package/dist/cjs/assets/assets.js +159 -0
  62. package/dist/cjs/assets/images.d.ts +31 -0
  63. package/dist/cjs/assets/images.js +246 -0
  64. package/dist/cjs/assets/png.d.ts +12 -0
  65. package/dist/cjs/assets/png.js +202 -0
  66. package/dist/cjs/assets/sounds.d.ts +37 -0
  67. package/dist/cjs/assets/sounds.js +246 -0
  68. package/dist/cjs/avm1/avm1.d.ts +55 -0
  69. package/dist/cjs/avm1/avm1.js +649 -0
  70. package/dist/cjs/avm1/project.d.ts +16 -0
  71. package/dist/cjs/avm1/project.js +88 -0
  72. package/dist/cjs/compiler/ast.d.ts +285 -0
  73. package/dist/cjs/compiler/ast.js +3 -0
  74. package/dist/cjs/compiler/class-compiler.d.ts +34 -0
  75. package/dist/cjs/compiler/class-compiler.js +440 -0
  76. package/dist/cjs/compiler/codegen.d.ts +144 -0
  77. package/dist/cjs/compiler/codegen.js +1589 -0
  78. package/dist/cjs/compiler/lexer.d.ts +27 -0
  79. package/dist/cjs/compiler/lexer.js +254 -0
  80. package/dist/cjs/compiler/parser.d.ts +58 -0
  81. package/dist/cjs/compiler/parser.js +989 -0
  82. package/dist/cjs/compiler/program.d.ts +29 -0
  83. package/dist/cjs/compiler/program.js +36 -0
  84. package/dist/cjs/compiler/scope.d.ts +22 -0
  85. package/dist/cjs/compiler/scope.js +242 -0
  86. package/dist/cjs/compression/lzma.d.ts +37 -0
  87. package/dist/cjs/compression/lzma.js +575 -0
  88. package/dist/cjs/compression/swf-container.d.ts +19 -0
  89. package/dist/cjs/compression/swf-container.js +92 -0
  90. package/dist/cjs/decompiler/ast.d.ts +228 -0
  91. package/dist/cjs/decompiler/ast.js +455 -0
  92. package/dist/cjs/decompiler/cfg.d.ts +40 -0
  93. package/dist/cjs/decompiler/cfg.js +236 -0
  94. package/dist/cjs/decompiler/class-printer.d.ts +9 -0
  95. package/dist/cjs/decompiler/class-printer.js +173 -0
  96. package/dist/cjs/decompiler/method.d.ts +41 -0
  97. package/dist/cjs/decompiler/method.js +436 -0
  98. package/dist/cjs/decompiler/simulate.d.ts +55 -0
  99. package/dist/cjs/decompiler/simulate.js +591 -0
  100. package/dist/cjs/decompiler/structure.d.ts +48 -0
  101. package/dist/cjs/decompiler/structure.js +764 -0
  102. package/dist/cjs/errors.d.ts +11 -0
  103. package/dist/cjs/errors.js +25 -0
  104. package/dist/cjs/index.d.ts +41 -0
  105. package/dist/cjs/index.js +82 -0
  106. package/dist/cjs/io/reader.d.ts +48 -0
  107. package/dist/cjs/io/reader.js +180 -0
  108. package/dist/cjs/io/writer.d.ts +37 -0
  109. package/dist/cjs/io/writer.js +175 -0
  110. package/dist/cjs/package.json +1 -0
  111. package/dist/cjs/project/pcode-project.d.ts +32 -0
  112. package/dist/cjs/project/pcode-project.js +210 -0
  113. package/dist/cjs/project/source-export.d.ts +12 -0
  114. package/dist/cjs/project/source-export.js +51 -0
  115. package/dist/cjs/project/source-import.d.ts +13 -0
  116. package/dist/cjs/project/source-import.js +73 -0
  117. package/dist/cjs/swf/swf.d.ts +63 -0
  118. package/dist/cjs/swf/swf.js +147 -0
  119. package/dist/cjs/swf/tag-codes.d.ts +77 -0
  120. package/dist/cjs/swf/tag-codes.js +109 -0
  121. package/dist/cjs/swf/tags.d.ts +118 -0
  122. package/dist/cjs/swf/tags.js +330 -0
  123. package/dist/cli.d.ts +2 -0
  124. package/dist/cli.js +296 -0
  125. package/dist/compiler/ast.d.ts +285 -0
  126. package/dist/compiler/ast.js +2 -0
  127. package/dist/compiler/class-compiler.d.ts +34 -0
  128. package/dist/compiler/class-compiler.js +437 -0
  129. package/dist/compiler/codegen.d.ts +144 -0
  130. package/dist/compiler/codegen.js +1585 -0
  131. package/dist/compiler/lexer.d.ts +27 -0
  132. package/dist/compiler/lexer.js +249 -0
  133. package/dist/compiler/parser.d.ts +58 -0
  134. package/dist/compiler/parser.js +984 -0
  135. package/dist/compiler/program.d.ts +29 -0
  136. package/dist/compiler/program.js +32 -0
  137. package/dist/compiler/scope.d.ts +22 -0
  138. package/dist/compiler/scope.js +238 -0
  139. package/dist/compression/lzma.d.ts +37 -0
  140. package/dist/compression/lzma.js +569 -0
  141. package/dist/compression/swf-container.d.ts +19 -0
  142. package/dist/compression/swf-container.js +87 -0
  143. package/dist/decompiler/ast.d.ts +228 -0
  144. package/dist/decompiler/ast.js +441 -0
  145. package/dist/decompiler/cfg.d.ts +40 -0
  146. package/dist/decompiler/cfg.js +231 -0
  147. package/dist/decompiler/class-printer.d.ts +9 -0
  148. package/dist/decompiler/class-printer.js +170 -0
  149. package/dist/decompiler/method.d.ts +41 -0
  150. package/dist/decompiler/method.js +427 -0
  151. package/dist/decompiler/simulate.d.ts +55 -0
  152. package/dist/decompiler/simulate.js +586 -0
  153. package/dist/decompiler/structure.d.ts +48 -0
  154. package/dist/decompiler/structure.js +760 -0
  155. package/dist/errors.d.ts +11 -0
  156. package/dist/errors.js +20 -0
  157. package/dist/index.d.ts +41 -0
  158. package/dist/index.js +49 -0
  159. package/dist/io/reader.d.ts +48 -0
  160. package/dist/io/reader.js +176 -0
  161. package/dist/io/writer.d.ts +37 -0
  162. package/dist/io/writer.js +169 -0
  163. package/dist/project/pcode-project.d.ts +32 -0
  164. package/dist/project/pcode-project.js +203 -0
  165. package/dist/project/source-export.d.ts +12 -0
  166. package/dist/project/source-export.js +47 -0
  167. package/dist/project/source-import.d.ts +13 -0
  168. package/dist/project/source-import.js +70 -0
  169. package/dist/swf/swf.d.ts +63 -0
  170. package/dist/swf/swf.js +143 -0
  171. package/dist/swf/tag-codes.d.ts +77 -0
  172. package/dist/swf/tag-codes.js +105 -0
  173. package/dist/swf/tags.d.ts +118 -0
  174. package/dist/swf/tags.js +316 -0
  175. package/package.json +71 -4
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatInstruction = formatInstruction;
4
+ exports.formatCode = formatCode;
5
+ exports.disassembleMethod = disassembleMethod;
6
+ const code_js_1 = require("../code.js");
7
+ const constants_js_1 = require("../constants.js");
8
+ const opcodes_js_1 = require("../opcodes.js");
9
+ const format_js_1 = require("./format.js");
10
+ const labelName = (id) => `L${id}`;
11
+ function hex(data) {
12
+ return Array.from(data, (b) => b.toString(16).padStart(2, '0')).join(' ');
13
+ }
14
+ /** Formats a single instruction (without indentation). */
15
+ function formatInstruction(abc, ins) {
16
+ const info = (0, opcodes_js_1.opInfo)(ins.op);
17
+ if (!info)
18
+ return `.bytes ${ins.op.toString(16).padStart(2, '0')}`;
19
+ const parts = [];
20
+ let a = 0;
21
+ let comment = '';
22
+ for (const t of info.operands) {
23
+ switch (t) {
24
+ case 'branch':
25
+ parts.push(labelName(ins.targets[0]));
26
+ break;
27
+ case 'switch':
28
+ parts.push(labelName(ins.targets[0]), `[${ins.targets.slice(1).map(labelName).join(', ')}]`);
29
+ break;
30
+ case 'int':
31
+ parts.push(String(abc.ints[ins.args[a++]] ?? 0));
32
+ break;
33
+ case 'uint':
34
+ parts.push(String(abc.uints[ins.args[a++]] ?? 0));
35
+ break;
36
+ case 'double':
37
+ parts.push((0, format_js_1.formatDouble)(abc.doubles[ins.args[a++]] ?? NaN));
38
+ break;
39
+ case 'string':
40
+ parts.push((0, format_js_1.formatString)(abc, ins.args[a++]));
41
+ break;
42
+ case 'namespace':
43
+ parts.push((0, format_js_1.formatNamespace)(abc, ins.args[a++]));
44
+ break;
45
+ case 'multiname':
46
+ parts.push((0, format_js_1.formatMultiname)(abc, ins.args[a++]));
47
+ break;
48
+ case 'class': {
49
+ const c = ins.args[a++];
50
+ parts.push(String(c));
51
+ comment = ` ; ${abc.className(c)}`;
52
+ break;
53
+ }
54
+ default:
55
+ parts.push(String(ins.args[a++]));
56
+ }
57
+ }
58
+ return parts.length ? `${info.name} ${parts.join(', ')}${comment}` : info.name + comment;
59
+ }
60
+ /** Formats decoded code items, one per line. */
61
+ function formatCode(abc, code, indent = ' ') {
62
+ const items = Array.isArray(code) ? code : code.items;
63
+ const lines = [];
64
+ for (const it of items) {
65
+ if (it.type === 'label')
66
+ lines.push(`${labelName(it.id)}:`);
67
+ else if (it.type === 'bytes') {
68
+ for (let i = 0; i < it.data.length; i += 16)
69
+ lines.push(`${indent}.bytes ${hex(it.data.subarray(i, i + 16))}`);
70
+ }
71
+ else
72
+ lines.push(indent + formatInstruction(abc, it));
73
+ }
74
+ return lines;
75
+ }
76
+ function formatFlags(flags) {
77
+ const names = [];
78
+ for (const [bit, name] of constants_js_1.METHOD_FLAG_NAMES) {
79
+ if (flags & bit)
80
+ names.push(name);
81
+ }
82
+ const known = [...constants_js_1.METHOD_FLAG_NAMES.keys()].reduce((x, y) => x | y, 0);
83
+ if (flags & ~known & 0xff)
84
+ names.push(`0x${(flags & ~known & 0xff).toString(16)}`);
85
+ return names.length ? names.join(' | ') : undefined;
86
+ }
87
+ function bodyLines(abc, body, ind) {
88
+ const lines = ['body'];
89
+ let decoded;
90
+ let error;
91
+ try {
92
+ decoded = (0, code_js_1.decodeCode)(body.code, body.exceptions);
93
+ }
94
+ catch (e) {
95
+ error = e.message;
96
+ }
97
+ lines.push(`${ind}maxstack ${body.maxStack}`);
98
+ lines.push(`${ind}localcount ${body.localCount}`);
99
+ lines.push(`${ind}initscopedepth ${body.initScopeDepth}`);
100
+ lines.push(`${ind}maxscopedepth ${body.maxScopeDepth}`);
101
+ for (const t of body.traits)
102
+ lines.push(ind + (0, format_js_1.formatTrait)(abc, t));
103
+ if (!decoded) {
104
+ // Not representable with labels: emit raw code and numeric exception offsets.
105
+ lines.push(`${ind}; WARNING: code could not be decoded (${error}); kept as raw bytes`);
106
+ for (const e of body.exceptions) {
107
+ lines.push(`${ind}try from @${e.from} to @${e.to} target @${e.target} type ${(0, format_js_1.formatMultiname)(abc, e.excType)} name ${(0, format_js_1.formatMultiname)(abc, e.varName)}`);
108
+ }
109
+ lines.push('code');
110
+ for (let i = 0; i < body.code.length; i += 16)
111
+ lines.push(`${ind}.bytes ${hex(body.code.subarray(i, i + 16))}`);
112
+ return lines;
113
+ }
114
+ for (const e of decoded.exceptions) {
115
+ lines.push(`${ind}try from ${labelName(e.from)} to ${labelName(e.to)} target ${labelName(e.target)} type ${(0, format_js_1.formatMultiname)(abc, e.excType)} name ${(0, format_js_1.formatMultiname)(abc, e.varName)}`);
116
+ }
117
+ lines.push('code');
118
+ lines.push(...formatCode(abc, decoded, ind));
119
+ return lines;
120
+ }
121
+ /**
122
+ * Disassembles a method (signature + body) to P-code text. The output can be
123
+ * edited and fed back to {@link assembleMethods}.
124
+ */
125
+ function disassembleMethod(abc, methodIndex, options = {}) {
126
+ const ind = options.indent ?? ' ';
127
+ const m = abc.methods[methodIndex];
128
+ if (!m)
129
+ throw new Error(`Method index ${methodIndex} out of range`);
130
+ const lines = [`method ${methodIndex}${options.title ? ` ; ${options.title}` : ''}`];
131
+ if (m.name !== 0)
132
+ lines.push(`${ind}name ${(0, format_js_1.formatString)(abc, m.name)}`);
133
+ lines.push(`${ind}returns ${(0, format_js_1.formatMultiname)(abc, m.returnType)}`);
134
+ for (const p of m.paramTypes)
135
+ lines.push(`${ind}param ${(0, format_js_1.formatMultiname)(abc, p)}`);
136
+ const flags = formatFlags(m.flags & ~(constants_js_1.MethodFlags.HAS_OPTIONAL | constants_js_1.MethodFlags.HAS_PARAM_NAMES));
137
+ if (flags)
138
+ lines.push(`${ind}flags ${flags}`);
139
+ if (m.flags & constants_js_1.MethodFlags.HAS_OPTIONAL && m.optional.length === 0)
140
+ lines.push(`${ind}flags HAS_OPTIONAL`);
141
+ for (const o of m.optional)
142
+ lines.push(`${ind}optional ${(0, format_js_1.formatValue)(abc, o.kind, o.value)}`);
143
+ for (const p of m.paramNames)
144
+ lines.push(`${ind}paramname ${(0, format_js_1.formatString)(abc, p)}`);
145
+ if (m.flags & constants_js_1.MethodFlags.HAS_PARAM_NAMES && m.paramNames.length === 0)
146
+ lines.push(`${ind}flags HAS_PARAM_NAMES`);
147
+ const body = abc.methodBody(methodIndex);
148
+ if (body)
149
+ lines.push(...bodyLines(abc, body, ind));
150
+ lines.push('end');
151
+ return lines.join('\n') + '\n';
152
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Text formatting of constant pool entries for P-code.
3
+ *
4
+ * Syntax (all of it is accepted back by the assembler):
5
+ * strings "text" (JSON escapes), null = string index 0
6
+ * namespaces PackageNamespace("flash.display"), PrivateNamespace("Foo", 12), * = index 0
7
+ * multinames QName(ns, "name"), QNameA(...), RTQName("n"), RTQNameA("n"), RTQNameL(), RTQNameLA(),
8
+ * Multiname("n", [ns, ...]), MultinameA(...), MultinameL([ns, ...]), MultinameLA(...),
9
+ * TypeName(QName(...), [QName(...), ...]), * = index 0
10
+ * values Integer(1), UInteger(1), Double(1.5), Utf8("s"), True, False, Null, Undefined,
11
+ * any namespace syntax
12
+ */
13
+ import type { AbcFile, Trait } from '../abc-file.js';
14
+ export declare function formatString(abc: AbcFile, index: number): string;
15
+ export declare function formatDouble(v: number): string;
16
+ export declare function formatNamespace(abc: AbcFile, index: number): string;
17
+ export declare function formatNsSet(abc: AbcFile, index: number): string;
18
+ export declare function formatMultiname(abc: AbcFile, index: number): string;
19
+ /** Constant value (optional parameter default / slot initial value). */
20
+ export declare function formatValue(abc: AbcFile, kind: number, index: number): string;
21
+ export declare const TRAIT_KIND_BY_TEXT: Map<string, number>;
22
+ /** One-line trait representation: `trait slot QName(...) slotid 1 type ... value ...`. */
23
+ export declare function formatTrait(abc: AbcFile, t: Trait): string;
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TRAIT_KIND_BY_TEXT = void 0;
4
+ exports.formatString = formatString;
5
+ exports.formatDouble = formatDouble;
6
+ exports.formatNamespace = formatNamespace;
7
+ exports.formatNsSet = formatNsSet;
8
+ exports.formatMultiname = formatMultiname;
9
+ exports.formatValue = formatValue;
10
+ exports.formatTrait = formatTrait;
11
+ const constants_js_1 = require("../constants.js");
12
+ function formatString(abc, index) {
13
+ if (index === 0)
14
+ return 'null';
15
+ const s = abc.strings[index];
16
+ return s === undefined ? `null ; invalid string index ${index}` : JSON.stringify(s);
17
+ }
18
+ function formatDouble(v) {
19
+ if (Number.isNaN(v))
20
+ return 'NaN';
21
+ if (v === Infinity)
22
+ return 'Infinity';
23
+ if (v === -Infinity)
24
+ return '-Infinity';
25
+ if (Object.is(v, -0))
26
+ return '-0';
27
+ return String(v);
28
+ }
29
+ function formatNamespace(abc, index) {
30
+ if (index === 0)
31
+ return '*';
32
+ const ns = abc.namespaces[index];
33
+ if (!ns)
34
+ return `* ; invalid namespace index ${index}`;
35
+ const kind = constants_js_1.NAMESPACE_KIND_NAMES.get(ns.kind) ?? `Namespace_0x${ns.kind.toString(16)}`;
36
+ const name = formatString(abc, ns.name);
37
+ if (ns.kind === constants_js_1.NamespaceKind.PrivateNs)
38
+ return `${kind}(${name}, ${index})`;
39
+ return `${kind}(${name})`;
40
+ }
41
+ function formatNsSet(abc, index) {
42
+ const set = abc.nsSets[index] ?? [];
43
+ return `[${set.map((n) => formatNamespace(abc, n)).join(', ')}]`;
44
+ }
45
+ function formatMultiname(abc, index) {
46
+ if (index === 0)
47
+ return '*';
48
+ const m = abc.multinames[index];
49
+ if (!m)
50
+ return `* ; invalid multiname index ${index}`;
51
+ const K = constants_js_1.MultinameKind;
52
+ switch (m.kind) {
53
+ case K.QName:
54
+ return `QName(${formatNamespace(abc, m.ns)}, ${formatString(abc, m.name)})`;
55
+ case K.QNameA:
56
+ return `QNameA(${formatNamespace(abc, m.ns)}, ${formatString(abc, m.name)})`;
57
+ case K.RTQName:
58
+ return `RTQName(${formatString(abc, m.name)})`;
59
+ case K.RTQNameA:
60
+ return `RTQNameA(${formatString(abc, m.name)})`;
61
+ case K.RTQNameL:
62
+ return 'RTQNameL()';
63
+ case K.RTQNameLA:
64
+ return 'RTQNameLA()';
65
+ case K.Multiname:
66
+ return `Multiname(${formatString(abc, m.name)}, ${formatNsSet(abc, m.nsSet)})`;
67
+ case K.MultinameA:
68
+ return `MultinameA(${formatString(abc, m.name)}, ${formatNsSet(abc, m.nsSet)})`;
69
+ case K.MultinameL:
70
+ return `MultinameL(${formatNsSet(abc, m.nsSet)})`;
71
+ case K.MultinameLA:
72
+ return `MultinameLA(${formatNsSet(abc, m.nsSet)})`;
73
+ case K.TypeName:
74
+ return `TypeName(${formatMultiname(abc, m.base)}, [${m.params.map((p) => formatMultiname(abc, p)).join(', ')}])`;
75
+ default:
76
+ return `* ; unknown multiname kind 0x${m.kind.toString(16)}`;
77
+ }
78
+ }
79
+ /** Constant value (optional parameter default / slot initial value). */
80
+ function formatValue(abc, kind, index) {
81
+ const C = constants_js_1.ConstantKind;
82
+ switch (kind) {
83
+ case C.Int:
84
+ return `Integer(${abc.ints[index] ?? 0})`;
85
+ case C.UInt:
86
+ return `UInteger(${abc.uints[index] ?? 0})`;
87
+ case C.Double:
88
+ return `Double(${formatDouble(abc.doubles[index] ?? NaN)})`;
89
+ case C.Utf8:
90
+ return `Utf8(${formatString(abc, index)})`;
91
+ case C.True:
92
+ case C.False:
93
+ case C.Null:
94
+ case C.Undefined: {
95
+ // Compilers store the kind itself as the (unused) index; keep any other value explicit.
96
+ const name = kind === C.True ? 'True' : kind === C.False ? 'False' : kind === C.Null ? 'Null' : 'Undefined';
97
+ return index === kind ? name : `${name}(${index})`;
98
+ }
99
+ default:
100
+ if (constants_js_1.NAMESPACE_KIND_NAMES.has(kind))
101
+ return formatNamespace(abc, index);
102
+ return `RawValue(${kind}, ${index})`;
103
+ }
104
+ }
105
+ const TRAIT_KIND_TEXT = {
106
+ [constants_js_1.TraitKind.Slot]: 'slot',
107
+ [constants_js_1.TraitKind.Const]: 'const',
108
+ [constants_js_1.TraitKind.Method]: 'method',
109
+ [constants_js_1.TraitKind.Getter]: 'getter',
110
+ [constants_js_1.TraitKind.Setter]: 'setter',
111
+ [constants_js_1.TraitKind.Class]: 'class',
112
+ [constants_js_1.TraitKind.Function]: 'function',
113
+ };
114
+ exports.TRAIT_KIND_BY_TEXT = new Map(Object.entries(TRAIT_KIND_TEXT).map(([k, v]) => [v, Number(k)]));
115
+ /** One-line trait representation: `trait slot QName(...) slotid 1 type ... value ...`. */
116
+ function formatTrait(abc, t) {
117
+ const parts = [`trait ${TRAIT_KIND_TEXT[t.kind] ?? `kind${t.kind}`} ${formatMultiname(abc, t.name)}`];
118
+ switch (t.kind) {
119
+ case constants_js_1.TraitKind.Slot:
120
+ case constants_js_1.TraitKind.Const:
121
+ parts.push(`slotid ${t.id}`, `type ${formatMultiname(abc, t.typeName)}`);
122
+ if (t.valueIndex !== 0 || t.valueKind !== 0)
123
+ parts.push(`value ${formatValue(abc, t.valueKind, t.valueIndex)}`);
124
+ break;
125
+ case constants_js_1.TraitKind.Class:
126
+ parts.push(`slotid ${t.id}`, `class ${t.classIndex}`);
127
+ break;
128
+ case constants_js_1.TraitKind.Function:
129
+ parts.push(`slotid ${t.id}`, `method ${t.method}`);
130
+ break;
131
+ default:
132
+ parts.push(`dispid ${t.id}`, `method ${t.method}`);
133
+ }
134
+ if (t.attrs & constants_js_1.TraitAttr.Final)
135
+ parts.push('final');
136
+ if (t.attrs & constants_js_1.TraitAttr.Override)
137
+ parts.push('override');
138
+ if (t.attrs & constants_js_1.TraitAttr.Metadata)
139
+ parts.push(`metadata [${t.metadata.join(', ')}]`);
140
+ return parts.join(' ');
141
+ }
@@ -0,0 +1,34 @@
1
+ import { AssemblerError } from '../../errors.js';
2
+ export type TokenType = 'id' | 'num' | 'str' | 'punct' | 'offset';
3
+ export interface Token {
4
+ type: TokenType;
5
+ /** Identifier text, punctuation char, or original number text. */
6
+ text: string;
7
+ /** Parsed value for numbers / strings / offsets. */
8
+ value?: number | string;
9
+ col: number;
10
+ }
11
+ /** Tokenises one line of P-code. `;` starts a comment outside of strings. */
12
+ export declare function tokenizeLine(line: string, lineNo: number, source?: string): Token[];
13
+ /** Cursor over the tokens of one line. */
14
+ export declare class LineCursor {
15
+ readonly tokens: Token[];
16
+ readonly line: number;
17
+ readonly source?: string | undefined;
18
+ pos: number;
19
+ constructor(tokens: Token[], line: number, source?: string | undefined);
20
+ get done(): boolean;
21
+ peek(offset?: number): Token | undefined;
22
+ error(message: string, tok?: Token | undefined): AssemblerError;
23
+ next(): Token;
24
+ isPunct(p: string): boolean;
25
+ isId(text?: string): boolean;
26
+ punct(p: string): void;
27
+ tryPunct(p: string): boolean;
28
+ id(expected?: string): string;
29
+ number(): number;
30
+ integer(min?: number, max?: number): number;
31
+ /** String literal or `null`. */
32
+ stringOrNull(): string | null;
33
+ end(): void;
34
+ }
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LineCursor = void 0;
4
+ exports.tokenizeLine = tokenizeLine;
5
+ const errors_js_1 = require("../../errors.js");
6
+ const PUNCT = new Set(['(', ')', '[', ']', ',', ':', '|', '*']);
7
+ const ID_START = /[A-Za-z_$]/;
8
+ const ID_PART = /[A-Za-z0-9_$.]/;
9
+ const NUM_RE = /^-?(?:0x[0-9a-fA-F]+|(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?)/;
10
+ /** Tokenises one line of P-code. `;` starts a comment outside of strings. */
11
+ function tokenizeLine(line, lineNo, source) {
12
+ const tokens = [];
13
+ const trimmed = line.trimStart();
14
+ if (trimmed.startsWith('.bytes')) {
15
+ // Raw byte directive: `.bytes 0a ff 3c ; comment`
16
+ const indent = line.length - trimmed.length;
17
+ tokens.push({ type: 'id', text: '.bytes', col: indent + 1 });
18
+ const re = /[^\s;]+|;/g;
19
+ let m;
20
+ re.lastIndex = indent + 6;
21
+ while ((m = re.exec(line))) {
22
+ if (m[0] === ';')
23
+ break;
24
+ tokens.push({ type: 'id', text: m[0], col: m.index + 1 });
25
+ }
26
+ return tokens;
27
+ }
28
+ let i = 0;
29
+ const n = line.length;
30
+ while (i < n) {
31
+ const c = line[i];
32
+ if (c === ' ' || c === '\t' || c === '\r') {
33
+ i++;
34
+ continue;
35
+ }
36
+ if (c === ';')
37
+ break;
38
+ const col = i + 1;
39
+ if (c === '"') {
40
+ let j = i + 1;
41
+ while (j < n && line[j] !== '"')
42
+ j += line[j] === '\\' ? 2 : 1;
43
+ if (j >= n)
44
+ throw new errors_js_1.AssemblerError('Unterminated string', lineNo, col, source);
45
+ const text = line.slice(i, j + 1);
46
+ let value;
47
+ try {
48
+ value = JSON.parse(text);
49
+ }
50
+ catch {
51
+ throw new errors_js_1.AssemblerError(`Invalid string literal ${text}`, lineNo, col, source);
52
+ }
53
+ tokens.push({ type: 'str', text, value, col });
54
+ i = j + 1;
55
+ continue;
56
+ }
57
+ if (c === '@') {
58
+ const m = /^@(\d+)/.exec(line.slice(i));
59
+ if (!m)
60
+ throw new errors_js_1.AssemblerError('Expected byte offset after @', lineNo, col, source);
61
+ tokens.push({ type: 'offset', text: m[0], value: Number(m[1]), col });
62
+ i += m[0].length;
63
+ continue;
64
+ }
65
+ const rest = line.slice(i);
66
+ if (/[-0-9.]/.test(c)) {
67
+ if (rest.startsWith('-Infinity')) {
68
+ tokens.push({ type: 'num', text: '-Infinity', value: -Infinity, col });
69
+ i += 9;
70
+ continue;
71
+ }
72
+ const m = NUM_RE.exec(rest);
73
+ if (m) {
74
+ const text = m[0];
75
+ const neg = text.startsWith('-');
76
+ const body = neg ? text.slice(1) : text;
77
+ let value = body.startsWith('0x') ? parseInt(body.slice(2), 16) : Number(body);
78
+ if (neg)
79
+ value = -value;
80
+ tokens.push({ type: 'num', text, value, col });
81
+ i += text.length;
82
+ continue;
83
+ }
84
+ }
85
+ if (ID_START.test(c)) {
86
+ let j = i + 1;
87
+ while (j < n && ID_PART.test(line[j]))
88
+ j++;
89
+ const text = line.slice(i, j);
90
+ if (text === 'NaN' || text === 'Infinity')
91
+ tokens.push({ type: 'num', text, value: text === 'NaN' ? NaN : Infinity, col });
92
+ else
93
+ tokens.push({ type: 'id', text, col });
94
+ i = j;
95
+ continue;
96
+ }
97
+ if (PUNCT.has(c)) {
98
+ tokens.push({ type: 'punct', text: c, col });
99
+ i++;
100
+ continue;
101
+ }
102
+ throw new errors_js_1.AssemblerError(`Unexpected character '${c}'`, lineNo, col, source);
103
+ }
104
+ return tokens;
105
+ }
106
+ /** Cursor over the tokens of one line. */
107
+ class LineCursor {
108
+ tokens;
109
+ line;
110
+ source;
111
+ pos = 0;
112
+ constructor(tokens, line, source) {
113
+ this.tokens = tokens;
114
+ this.line = line;
115
+ this.source = source;
116
+ }
117
+ get done() {
118
+ return this.pos >= this.tokens.length;
119
+ }
120
+ peek(offset = 0) {
121
+ return this.tokens[this.pos + offset];
122
+ }
123
+ error(message, tok = this.peek()) {
124
+ const col = tok?.col ?? (this.tokens.length ? this.tokens[this.tokens.length - 1].col + this.tokens[this.tokens.length - 1].text.length : 1);
125
+ return new errors_js_1.AssemblerError(message, this.line, col, this.source);
126
+ }
127
+ next() {
128
+ const t = this.tokens[this.pos];
129
+ if (!t)
130
+ throw this.error('Unexpected end of line');
131
+ this.pos++;
132
+ return t;
133
+ }
134
+ isPunct(p) {
135
+ const t = this.peek();
136
+ return t?.type === 'punct' && t.text === p;
137
+ }
138
+ isId(text) {
139
+ const t = this.peek();
140
+ return t?.type === 'id' && (text === undefined || t.text === text);
141
+ }
142
+ punct(p) {
143
+ const t = this.next();
144
+ if (t.type !== 'punct' || t.text !== p)
145
+ throw this.error(`Expected '${p}'`, t);
146
+ }
147
+ tryPunct(p) {
148
+ if (this.isPunct(p)) {
149
+ this.pos++;
150
+ return true;
151
+ }
152
+ return false;
153
+ }
154
+ id(expected) {
155
+ const t = this.next();
156
+ if (t.type !== 'id' || (expected !== undefined && t.text !== expected)) {
157
+ throw this.error(expected ? `Expected '${expected}'` : 'Expected identifier', t);
158
+ }
159
+ return t.text;
160
+ }
161
+ number() {
162
+ const t = this.next();
163
+ if (t.type !== 'num')
164
+ throw this.error('Expected number', t);
165
+ return t.value;
166
+ }
167
+ integer(min = -Infinity, max = Infinity) {
168
+ const tok = this.peek();
169
+ const v = this.number();
170
+ if (!Number.isInteger(v) || v < min || v > max)
171
+ throw this.error(`Expected integer in range ${min}..${max}`, tok);
172
+ return v;
173
+ }
174
+ /** String literal or `null`. */
175
+ stringOrNull() {
176
+ const t = this.next();
177
+ if (t.type === 'str')
178
+ return t.value;
179
+ if (t.type === 'id' && t.text === 'null')
180
+ return null;
181
+ throw this.error('Expected string or null', t);
182
+ }
183
+ end() {
184
+ if (!this.done)
185
+ throw this.error(`Unexpected '${this.peek().text}'`);
186
+ }
187
+ }
188
+ exports.LineCursor = LineCursor;
@@ -0,0 +1,26 @@
1
+ import type { Swf } from '../swf/swf.js';
2
+ import { RawTag, type Tag } from '../swf/tags.js';
3
+ export type AssetKind = 'images' | 'sounds' | 'binary' | 'texts';
4
+ export declare const ALL_ASSET_KINDS: AssetKind[];
5
+ export interface ExportAssetsResult {
6
+ files: string[];
7
+ /** Characters that could not be exported, with the reason. */
8
+ skipped: Array<{
9
+ id: number;
10
+ reason: string;
11
+ }>;
12
+ }
13
+ /** Class / export names per character id (SymbolClass + ExportAssets). */
14
+ export declare function symbolNames(swf: Swf): Map<number, string[]>;
15
+ /** Initial text of a DefineEditText tag (undefined when it has none). */
16
+ export declare function editTextOf(tag: RawTag): {
17
+ text: string;
18
+ offset: number;
19
+ end: number;
20
+ } | undefined;
21
+ /** Writes resources into `outDir/<kind>/<id>[_<symbol>].<ext>`. */
22
+ export declare function exportAssets(swf: Swf, outDir: string, kinds?: AssetKind[]): Promise<ExportAssetsResult>;
23
+ /** Replaces a DefineEditText's initial text. */
24
+ export declare function replaceEditText(swf: Swf, id: number, text: string): Tag;
25
+ /** Replaces the resource with character `id` using a file's contents (type chosen by the tag). */
26
+ export declare function replaceAsset(swf: Swf, id: number, file: Uint8Array): Tag;