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
package/dist/cli.js ADDED
@@ -0,0 +1,296 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from 'node:util';
3
+ import { readFile, writeFile } from 'node:fs/promises';
4
+ import { Swf } from './swf/swf.js';
5
+ import { DefineSpriteTag, DoABCTag } from './swf/tags.js';
6
+ import { packSwf, unpackSwf } from './compression/swf-container.js';
7
+ import { classMethods, describeMethodRef, scriptMethods, scriptOfClass } from './abc/model.js';
8
+ import { disassembleMethod } from './abc/pcode/disassembler.js';
9
+ import { exportPcode, importPcode } from './project/pcode-project.js';
10
+ import { decompileClass } from './decompiler/class-printer.js';
11
+ import { exportSources } from './project/source-export.js';
12
+ import { importSources } from './project/source-import.js';
13
+ import { compileSources, createSwf } from './compiler/program.js';
14
+ import { exportAs2, importAs2 } from './avm1/project.js';
15
+ import { ALL_ASSET_KINDS, exportAssets, replaceAsset } from './assets/assets.js';
16
+ const HELP = `swf-toolkit <command> [options]
17
+
18
+ Inspect
19
+ info <file.swf> Header, tag statistics, ABC summary
20
+ tags <file.swf> List tags (nested sprite tags indented)
21
+ classes <file.swf> List AS3 classes
22
+ disasm <file.swf> <class> [method] Print P-code of a class or one of its methods
23
+ decompile <file.swf> <class> Print decompiled ActionScript 3 source of a class
24
+
25
+ Edit
26
+ export-pcode <file.swf> <dir> Write one editable .pcode file per class
27
+ import-pcode <file.swf> <dir> <out.swf> Apply edited .pcode files and save
28
+ export-as3 <file.swf> <dir> Write decompiled, editable .as sources
29
+ import-as3 <file.swf> <dir> <out.swf> Compile edited / new .as files and save
30
+ compile <file.swf> <out.swf> <a.as>... Compile the given .as files into the SWF
31
+
32
+ Build
33
+ build <out.swf> <Main.as> [more.as...] Compile sources into a new SWF
34
+ [--main <Class>] [--width N] [--height N]
35
+
36
+ AS1 / AS2
37
+ export-as2 <file.swf> <dir> Write DoAction / DoInitAction blocks as P-code
38
+ import-as2 <file.swf> <dir> <out.swf> Re-assemble edited AS1/AS2 P-code and save
39
+
40
+ Assets
41
+ export-assets <file.swf> <dir> [kinds] Export images, sounds, binary, texts (kinds: comma list)
42
+ replace <in.swf> <out.swf> <id> <file> [<id> <file>...]
43
+ Replace bitmaps (png/jpg/gif), sounds (mp3/wav),
44
+ binary data or edit-text strings by character id
45
+
46
+ Container
47
+ decompress <in.swf> <out.swf> Save uncompressed (FWS)
48
+ compress <in.swf> <out.swf> [zlib|lzma] Save compressed (default zlib)
49
+
50
+ Options
51
+ --compression <none|zlib|lzma> Output compression for import-pcode
52
+ -h, --help Show this help
53
+ `;
54
+ function fail(msg) {
55
+ process.stderr.write(`swf-toolkit: ${msg}\n`);
56
+ process.exit(1);
57
+ }
58
+ function compressionArg(v, fallback) {
59
+ if (v === undefined)
60
+ return fallback;
61
+ if (v === 'none' || v === 'zlib' || v === 'lzma')
62
+ return v;
63
+ fail(`invalid compression '${v}' (expected none, zlib or lzma)`);
64
+ }
65
+ function tagLine(tag, index, depth) {
66
+ const id = tag.characterId;
67
+ const len = tag.encodeBody().length;
68
+ const extra = tag instanceof DoABCTag ? ` name="${tag.abcName}"` : tag instanceof DefineSpriteTag ? ` frames=${tag.frameCount}` : '';
69
+ return `${' '.repeat(depth)}${String(index).padStart(5)} ${tag.name}${id !== undefined ? ` (id ${id})` : ''} len=${len}${extra}`;
70
+ }
71
+ async function main() {
72
+ const { values, positionals } = parseArgs({
73
+ allowPositionals: true,
74
+ options: {
75
+ help: { type: 'boolean', short: 'h' },
76
+ compression: { type: 'string' },
77
+ main: { type: 'string' },
78
+ width: { type: 'string' },
79
+ height: { type: 'string' },
80
+ },
81
+ });
82
+ const [cmd, ...args] = positionals;
83
+ if (values.help || !cmd) {
84
+ process.stdout.write(HELP);
85
+ return;
86
+ }
87
+ const need = (n, usage) => {
88
+ if (args.length < n)
89
+ fail(`usage: swf-toolkit ${usage}`);
90
+ };
91
+ switch (cmd) {
92
+ case 'info': {
93
+ need(1, 'info <file.swf>');
94
+ const swf = await Swf.load(args[0]);
95
+ const counts = new Map();
96
+ for (const { tag } of swf.walkTags())
97
+ counts.set(tag.name, (counts.get(tag.name) ?? 0) + 1);
98
+ const out = [
99
+ `version ${swf.version}`,
100
+ `compression ${swf.compression}`,
101
+ `stage ${swf.width} x ${swf.height} px`,
102
+ `frame rate ${swf.frameRate}`,
103
+ `frames ${swf.frameCount}`,
104
+ `tags ${swf.tags.length} top-level`,
105
+ ];
106
+ if (swf.documentClass)
107
+ out.push(`document ${swf.documentClass}`);
108
+ for (const tag of swf.abcTags) {
109
+ const abc = tag.abc;
110
+ out.push(`abc "${tag.abcName}" v${abc.majorVersion}.${abc.minorVersion} ${abc.instances.length} classes, ${abc.methods.length} methods, ${abc.bodies.length} bodies`);
111
+ }
112
+ out.push('', 'tag counts:');
113
+ for (const [name, n] of [...counts].sort((a, b) => b[1] - a[1]))
114
+ out.push(` ${String(n).padStart(6)} ${name}`);
115
+ process.stdout.write(out.join('\n') + '\n');
116
+ break;
117
+ }
118
+ case 'tags': {
119
+ need(1, 'tags <file.swf>');
120
+ const swf = await Swf.load(args[0]);
121
+ const lines = [];
122
+ const walk = (tags, depth) => {
123
+ tags.forEach((t, i) => {
124
+ lines.push(tagLine(t, i, depth));
125
+ if (t instanceof DefineSpriteTag)
126
+ walk(t.tags, depth + 1);
127
+ });
128
+ };
129
+ walk(swf.tags, 0);
130
+ process.stdout.write(lines.join('\n') + '\n');
131
+ break;
132
+ }
133
+ case 'classes': {
134
+ need(1, 'classes <file.swf>');
135
+ const swf = await Swf.load(args[0]);
136
+ const lines = [];
137
+ for (const tag of swf.abcTags)
138
+ for (const name of tag.abc.classNames())
139
+ lines.push(name);
140
+ process.stdout.write(lines.join('\n') + '\n');
141
+ break;
142
+ }
143
+ case 'disasm': {
144
+ need(2, 'disasm <file.swf> <class> [method]');
145
+ const swf = await Swf.load(args[0]);
146
+ const [, className, methodName] = args;
147
+ for (const tag of swf.abcTags) {
148
+ const abc = tag.abc;
149
+ const ci = abc.findClass(className);
150
+ if (ci < 0)
151
+ continue;
152
+ const s = scriptOfClass(abc, ci);
153
+ const refs = [...(s >= 0 ? scriptMethods(abc, s, false) : []), ...classMethods(abc, ci)];
154
+ const selected = methodName
155
+ ? refs.filter((r) => r.name === methodName || (methodName === 'constructor' && r.role === 'constructor') || (methodName === 'cinit' && r.role === 'class-init'))
156
+ : refs;
157
+ if (!selected.length)
158
+ fail(`method '${methodName}' not found in ${className}`);
159
+ process.stdout.write(selected.map((r) => disassembleMethod(abc, r.methodIndex, { title: describeMethodRef(abc, className, r) })).join('\n'));
160
+ return;
161
+ }
162
+ fail(`class '${className}' not found`);
163
+ }
164
+ case 'decompile': {
165
+ need(2, 'decompile <file.swf> <class>');
166
+ const swf = await Swf.load(args[0]);
167
+ for (const tag of swf.abcTags) {
168
+ const ci = tag.abc.findClass(args[1]);
169
+ if (ci >= 0) {
170
+ process.stdout.write(decompileClass(tag.abc, ci));
171
+ return;
172
+ }
173
+ }
174
+ fail(`class '${args[1]}' not found`);
175
+ }
176
+ case 'export-pcode': {
177
+ need(2, 'export-pcode <file.swf> <dir>');
178
+ const swf = await Swf.load(args[0]);
179
+ const r = await exportPcode(swf, args[1]);
180
+ process.stdout.write(`Exported ${r.methodCount} methods in ${r.files.length} files to ${r.outDir}\n`);
181
+ break;
182
+ }
183
+ case 'import-pcode': {
184
+ need(3, 'import-pcode <file.swf> <dir> <out.swf>');
185
+ const swf = await Swf.load(args[0]);
186
+ const r = await importPcode(swf, args[1]);
187
+ await swf.save(args[2], { compression: compressionArg(values.compression) });
188
+ for (const c of r.changed)
189
+ process.stdout.write(` updated method ${c.methodIndex} (${c.file})\n`);
190
+ process.stdout.write(`${r.changed.length} method(s) updated, ${r.unchanged} unchanged. Saved ${args[2]}\n`);
191
+ break;
192
+ }
193
+ case 'export-as3': {
194
+ need(2, 'export-as3 <file.swf> <dir>');
195
+ const swf = await Swf.load(args[0]);
196
+ const r = await exportSources(swf, args[1]);
197
+ process.stdout.write(`Wrote ${r.files.length} files to ${args[1]}${r.failed.length ? ` (${r.failed.length} classes failed, see comments in files)` : ''}\n`);
198
+ break;
199
+ }
200
+ case 'import-as3':
201
+ case 'compile': {
202
+ const isImport = cmd === 'import-as3';
203
+ need(3, isImport ? 'import-as3 <file.swf> <dir> <out.swf>' : 'compile <file.swf> <out.swf> <a.as> [b.as...]');
204
+ const swf = await Swf.load(args[0]);
205
+ const out = isImport ? args[2] : args[1];
206
+ const r = isImport ? await importSources(swf, args[1]) : await importSources(swf, process.cwd(), { files: args.slice(2) });
207
+ await swf.save(out, { compression: compressionArg(values.compression) });
208
+ for (const c of r.changes)
209
+ process.stdout.write(` ${c.action.padEnd(8)} ${c.className}${c.member === '<class>' ? '' : ' :: ' + c.member}\n`);
210
+ process.stdout.write(`${r.changes.length} change(s). Saved ${out}\n`);
211
+ break;
212
+ }
213
+ case 'build': {
214
+ need(2, 'build <out.swf> <Main.as> [more.as...]');
215
+ const files = await Promise.all(args.slice(1).map(async (f) => ({ name: f, text: await readFile(f, 'utf8') })));
216
+ const { abc } = compileSources(files);
217
+ const main = values.main ?? (() => {
218
+ const first = args[1].split('/').pop().replace(/\.as$/, '');
219
+ const ci = abc.instances.findIndex((_, i) => abc.className(i).split('.').pop() === first);
220
+ return ci >= 0 ? abc.className(ci) : first;
221
+ })();
222
+ const swf = createSwf(abc, {
223
+ documentClass: main,
224
+ width: values.width ? Number(values.width) : undefined,
225
+ height: values.height ? Number(values.height) : undefined,
226
+ });
227
+ await swf.save(args[0], { compression: compressionArg(values.compression) });
228
+ process.stdout.write(`Built ${args[0]} (${abc.instances.length} classes, document class ${main})\n`);
229
+ break;
230
+ }
231
+ case 'export-as2': {
232
+ need(2, 'export-as2 <file.swf> <dir>');
233
+ const files = await exportAs2(await Swf.load(args[0]), args[1]);
234
+ process.stdout.write(`Wrote ${files.length} AS1/AS2 blocks to ${args[1]}\n`);
235
+ break;
236
+ }
237
+ case 'import-as2': {
238
+ need(3, 'import-as2 <file.swf> <dir> <out.swf>');
239
+ const swf = await Swf.load(args[0]);
240
+ const changed = await importAs2(swf, args[1]);
241
+ await swf.save(args[2], { compression: compressionArg(values.compression) });
242
+ for (const k of changed)
243
+ process.stdout.write(` updated ${k}\n`);
244
+ process.stdout.write(`${changed.length} block(s) updated. Saved ${args[2]}\n`);
245
+ break;
246
+ }
247
+ case 'export-assets': {
248
+ need(2, 'export-assets <file.swf> <dir> [images,sounds,binary,texts]');
249
+ const swf = await Swf.load(args[0]);
250
+ const kinds = (args[2] ? args[2].split(',') : ALL_ASSET_KINDS);
251
+ const r = await exportAssets(swf, args[1], kinds);
252
+ for (const s of r.skipped)
253
+ process.stdout.write(` skipped ${s.id}: ${s.reason}\n`);
254
+ process.stdout.write(`Exported ${r.files.length} files to ${args[1]}\n`);
255
+ break;
256
+ }
257
+ case 'replace': {
258
+ need(4, 'replace <in.swf> <out.swf> <id> <file> [<id> <file>...]');
259
+ if ((args.length - 2) % 2)
260
+ fail('expected <id> <file> pairs');
261
+ const swf = await Swf.load(args[0]);
262
+ for (let i = 2; i < args.length; i += 2) {
263
+ const id = Number(args[i]);
264
+ if (!Number.isInteger(id))
265
+ fail(`invalid character id '${args[i]}'`);
266
+ const tag = replaceAsset(swf, id, new Uint8Array(await readFile(args[i + 1])));
267
+ process.stdout.write(` ${id} <- ${args[i + 1]} (${tag.name})\n`);
268
+ }
269
+ await swf.save(args[1], { compression: compressionArg(values.compression) });
270
+ process.stdout.write(`Saved ${args[1]}\n`);
271
+ break;
272
+ }
273
+ case 'decompress':
274
+ case 'compress': {
275
+ need(2, `${cmd} <in.swf> <out.swf>`);
276
+ const c = unpackSwf(new Uint8Array(await readFile(args[0])));
277
+ const compression = cmd === 'decompress' ? 'none' : compressionArg(args[2], 'zlib');
278
+ const out = packSwf({ ...c, compression });
279
+ await writeFile(args[1], out);
280
+ process.stdout.write(`${args[0]} (${c.compression}) -> ${args[1]} (${compression}, ${out.length} bytes)\n`);
281
+ break;
282
+ }
283
+ default:
284
+ fail(`unknown command '${cmd}' (see --help)`);
285
+ }
286
+ }
287
+ // Exit quietly when output is piped into e.g. `head`.
288
+ process.stdout.on('error', (e) => {
289
+ if (e.code === 'EPIPE')
290
+ process.exit(0);
291
+ throw e;
292
+ });
293
+ main().catch((e) => {
294
+ const err = e;
295
+ fail(err.message ?? String(e));
296
+ });
@@ -0,0 +1,285 @@
1
+ /** AST produced by the AS3 parser. */
2
+ export interface Pos {
3
+ line: number;
4
+ col: number;
5
+ }
6
+ export interface TypeRef {
7
+ name: string;
8
+ /** Vector.<T> parameters. */
9
+ params?: TypeRef[];
10
+ pos: Pos;
11
+ }
12
+ export type Expr = ({
13
+ k: 'num';
14
+ v: number;
15
+ } | {
16
+ k: 'str';
17
+ v: string;
18
+ } | {
19
+ k: 'bool';
20
+ v: boolean;
21
+ } | {
22
+ k: 'null';
23
+ } | {
24
+ k: 'undefined';
25
+ } | {
26
+ k: 'regex';
27
+ pattern: string;
28
+ flags: string;
29
+ } | {
30
+ k: 'id';
31
+ name: string;
32
+ ns?: string;
33
+ } | {
34
+ k: 'this';
35
+ } | {
36
+ k: 'super';
37
+ } | {
38
+ k: 'member';
39
+ obj: Expr;
40
+ name: string;
41
+ ns?: string;
42
+ attr?: boolean;
43
+ } | {
44
+ k: 'index';
45
+ obj: Expr;
46
+ index: Expr;
47
+ attr?: boolean;
48
+ } | {
49
+ k: 'descendants';
50
+ obj: Expr;
51
+ name: string;
52
+ attr?: boolean;
53
+ } | {
54
+ k: 'filter';
55
+ obj: Expr;
56
+ cond: Expr;
57
+ } | {
58
+ k: 'typeapply';
59
+ base: Expr;
60
+ params: TypeRef[];
61
+ } | {
62
+ k: 'call';
63
+ fn: Expr;
64
+ args: Expr[];
65
+ } | {
66
+ k: 'new';
67
+ ctor: Expr;
68
+ args: Expr[];
69
+ } | {
70
+ k: 'vector';
71
+ type: TypeRef;
72
+ items: Expr[];
73
+ } | {
74
+ k: 'unary';
75
+ op: string;
76
+ e: Expr;
77
+ } | {
78
+ k: 'update';
79
+ op: '++' | '--';
80
+ prefix: boolean;
81
+ e: Expr;
82
+ } | {
83
+ k: 'binary';
84
+ op: string;
85
+ a: Expr;
86
+ b: Expr;
87
+ } | {
88
+ k: 'cond';
89
+ c: Expr;
90
+ t: Expr;
91
+ f: Expr;
92
+ } | {
93
+ k: 'assign';
94
+ op: string;
95
+ target: Expr;
96
+ v: Expr;
97
+ } | {
98
+ k: 'array';
99
+ items: Array<Expr | null>;
100
+ } | {
101
+ k: 'object';
102
+ props: Array<{
103
+ key: string | number;
104
+ value: Expr;
105
+ }>;
106
+ } | {
107
+ k: 'function';
108
+ fn: FunctionDef;
109
+ } | {
110
+ k: 'comma';
111
+ list: Expr[];
112
+ } | {
113
+ k: 'xml';
114
+ text: string;
115
+ }) & {
116
+ pos: Pos;
117
+ };
118
+ export interface Param {
119
+ name: string;
120
+ type?: TypeRef;
121
+ init?: Expr;
122
+ }
123
+ export interface FunctionDef {
124
+ name?: string;
125
+ params: Param[];
126
+ rest?: {
127
+ name: string;
128
+ type?: TypeRef;
129
+ };
130
+ returnType?: TypeRef;
131
+ body?: Stmt[];
132
+ pos: Pos;
133
+ }
134
+ export interface VarDecl {
135
+ name: string;
136
+ type?: TypeRef;
137
+ init?: Expr;
138
+ pos: Pos;
139
+ }
140
+ export interface CatchClause {
141
+ name: string;
142
+ type?: TypeRef;
143
+ body: Stmt[];
144
+ }
145
+ export type Stmt = ({
146
+ k: 'expr';
147
+ e: Expr;
148
+ } | {
149
+ k: 'var';
150
+ isConst: boolean;
151
+ decls: VarDecl[];
152
+ } | {
153
+ k: 'function';
154
+ fn: FunctionDef;
155
+ } | {
156
+ k: 'if';
157
+ c: Expr;
158
+ then: Stmt;
159
+ else?: Stmt;
160
+ } | {
161
+ k: 'while';
162
+ c: Expr;
163
+ body: Stmt;
164
+ } | {
165
+ k: 'dowhile';
166
+ c: Expr;
167
+ body: Stmt;
168
+ } | {
169
+ k: 'for';
170
+ init?: Stmt;
171
+ c?: Expr;
172
+ update?: Expr;
173
+ body: Stmt;
174
+ } | {
175
+ k: 'forin';
176
+ each: boolean;
177
+ decl?: VarDecl;
178
+ target?: Expr;
179
+ obj: Expr;
180
+ body: Stmt;
181
+ } | {
182
+ k: 'switch';
183
+ disc: Expr;
184
+ cases: Array<{
185
+ test: Expr | null;
186
+ body: Stmt[];
187
+ }>;
188
+ } | {
189
+ k: 'try';
190
+ body: Stmt[];
191
+ catches: CatchClause[];
192
+ finally?: Stmt[];
193
+ } | {
194
+ k: 'return';
195
+ e?: Expr;
196
+ } | {
197
+ k: 'throw';
198
+ e: Expr;
199
+ } | {
200
+ k: 'break';
201
+ label?: string;
202
+ } | {
203
+ k: 'continue';
204
+ label?: string;
205
+ } | {
206
+ k: 'block';
207
+ body: Stmt[];
208
+ } | {
209
+ k: 'labeled';
210
+ label: string;
211
+ body: Stmt;
212
+ } | {
213
+ k: 'with';
214
+ obj: Expr;
215
+ body: Stmt;
216
+ } | {
217
+ k: 'dxns';
218
+ e: Expr;
219
+ } | {
220
+ k: 'empty';
221
+ }) & {
222
+ pos: Pos;
223
+ };
224
+ export interface Metadata {
225
+ name: string;
226
+ items: Array<{
227
+ key?: string;
228
+ value: string;
229
+ }>;
230
+ }
231
+ export interface FieldMember {
232
+ k: 'field';
233
+ name: string;
234
+ isStatic: boolean;
235
+ isConst: boolean;
236
+ access: string;
237
+ type?: TypeRef;
238
+ init?: Expr;
239
+ metadata: Metadata[];
240
+ pos: Pos;
241
+ /** Source span [start, end) of the declaration. */
242
+ span: [number, number];
243
+ }
244
+ export interface MethodMember {
245
+ k: 'method';
246
+ name: string;
247
+ kind: 'method' | 'get' | 'set' | 'constructor';
248
+ isStatic: boolean;
249
+ isOverride: boolean;
250
+ isFinal: boolean;
251
+ isNative: boolean;
252
+ access: string;
253
+ fn: FunctionDef;
254
+ metadata: Metadata[];
255
+ pos: Pos;
256
+ span: [number, number];
257
+ }
258
+ export interface StaticBlockMember {
259
+ k: 'static';
260
+ body: Stmt[];
261
+ pos: Pos;
262
+ span: [number, number];
263
+ }
264
+ export type Member = FieldMember | MethodMember | StaticBlockMember;
265
+ export interface ClassDef {
266
+ name: string;
267
+ isInterface: boolean;
268
+ isDynamic: boolean;
269
+ isFinal: boolean;
270
+ access: string;
271
+ extends?: TypeRef;
272
+ implements: TypeRef[];
273
+ members: Member[];
274
+ metadata: Metadata[];
275
+ pos: Pos;
276
+ }
277
+ export interface CompilationUnit {
278
+ package: string;
279
+ imports: string[];
280
+ /** `use namespace x` declarations. */
281
+ useNamespaces: string[];
282
+ classes: ClassDef[];
283
+ /** Package-level functions / variables (rare). */
284
+ functions: MethodMember[];
285
+ }
@@ -0,0 +1,2 @@
1
+ /** AST produced by the AS3 parser. */
2
+ export {};
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Compiles an edited AS3 class back into an existing ABC.
3
+ *
4
+ * Only members whose source text changed compared with a baseline (by
5
+ * default the decompiled text of the current bytecode) are recompiled; all
6
+ * other methods keep their original bytecode.
7
+ */
8
+ import type { AbcFile } from '../abc/abc-file.js';
9
+ import { Interner } from '../abc/interner.js';
10
+ export interface CompileClassOptions {
11
+ /** File name used in error messages. */
12
+ source?: string;
13
+ /**
14
+ * Baseline source the edit started from. Members identical to the baseline
15
+ * are not recompiled. Defaults to the decompiled class; pass `null` to
16
+ * recompile every member.
17
+ */
18
+ baseline?: string | null;
19
+ /** Reuse an interner across calls on the same ABC. */
20
+ interner?: Interner;
21
+ /** Exact class index to compile into (for ABCs with duplicate class names). */
22
+ classIndex?: number;
23
+ }
24
+ export interface CompiledMember {
25
+ className: string;
26
+ member: string;
27
+ action: 'replaced' | 'added';
28
+ methodIndex?: number;
29
+ }
30
+ export interface CompileClassResult {
31
+ changes: CompiledMember[];
32
+ }
33
+ /** Compiles every class in `source` into `abc`. */
34
+ export declare function compileClassSource(abc: AbcFile, source: string, options?: CompileClassOptions): CompileClassResult;