sci2-ts 0.0.0-stage → 0.2.2
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/CHANGELOG.md +78 -0
- package/LICENSE +21 -0
- package/README.md +113 -2
- package/apps/viewer/editor.html +43 -0
- package/apps/viewer/explore.html +27 -0
- package/apps/viewer/index.html +26 -0
- package/apps/viewer/package.json +27 -0
- package/apps/viewer/play.html +26 -0
- package/apps/viewer/plugin.d.ts +18 -0
- package/apps/viewer/plugin.js +75 -0
- package/apps/viewer/plugin.ts +84 -0
- package/apps/viewer/src/adlib.d.ts +20 -0
- package/apps/viewer/src/adlib.js +55 -0
- package/apps/viewer/src/adlib.ts +62 -0
- package/apps/viewer/src/app.d.ts +5 -0
- package/apps/viewer/src/app.js +5 -0
- package/apps/viewer/src/app.ts +5 -0
- package/apps/viewer/src/asset-index.d.ts +22 -0
- package/apps/viewer/src/asset-index.js +74 -0
- package/apps/viewer/src/asset-index.ts +90 -0
- package/apps/viewer/src/audio.d.ts +28 -0
- package/apps/viewer/src/audio.js +102 -0
- package/apps/viewer/src/audio.ts +110 -0
- package/apps/viewer/src/editor-handles.d.ts +63 -0
- package/apps/viewer/src/editor-handles.js +332 -0
- package/apps/viewer/src/editor-handles.ts +368 -0
- package/apps/viewer/src/editor-languages.d.ts +23 -0
- package/apps/viewer/src/editor-languages.js +157 -0
- package/apps/viewer/src/editor-languages.ts +135 -0
- package/apps/viewer/src/editor.css +213 -0
- package/apps/viewer/src/editor.d.ts +1 -0
- package/apps/viewer/src/editor.js +393 -0
- package/apps/viewer/src/editor.ts +412 -0
- package/apps/viewer/src/explore/classes.d.ts +7 -0
- package/apps/viewer/src/explore/classes.js +63 -0
- package/apps/viewer/src/explore/classes.ts +61 -0
- package/apps/viewer/src/explore/context.d.ts +33 -0
- package/apps/viewer/src/explore/context.js +96 -0
- package/apps/viewer/src/explore/context.ts +124 -0
- package/apps/viewer/src/explore/explore.css +93 -0
- package/apps/viewer/src/explore/explore.d.ts +1 -0
- package/apps/viewer/src/explore/explore.js +102 -0
- package/apps/viewer/src/explore/explore.ts +104 -0
- package/apps/viewer/src/explore/inspect.d.ts +5 -0
- package/apps/viewer/src/explore/inspect.js +208 -0
- package/apps/viewer/src/explore/inspect.ts +279 -0
- package/apps/viewer/src/explore/live.d.ts +22 -0
- package/apps/viewer/src/explore/live.js +318 -0
- package/apps/viewer/src/explore/live.ts +365 -0
- package/apps/viewer/src/explore/map.d.ts +11 -0
- package/apps/viewer/src/explore/map.js +182 -0
- package/apps/viewer/src/explore/map.ts +193 -0
- package/apps/viewer/src/files.d.ts +21 -0
- package/apps/viewer/src/files.js +69 -0
- package/apps/viewer/src/files.ts +72 -0
- package/apps/viewer/src/game-profile.d.ts +19 -0
- package/apps/viewer/src/game-profile.js +4 -0
- package/apps/viewer/src/game-profile.ts +26 -0
- package/apps/viewer/src/main.d.ts +1 -0
- package/apps/viewer/src/main.js +473 -0
- package/apps/viewer/src/main.ts +527 -0
- package/apps/viewer/src/music.d.ts +14 -0
- package/apps/viewer/src/music.js +32 -0
- package/apps/viewer/src/music.ts +34 -0
- package/apps/viewer/src/opl-worklet.d.ts +1 -0
- package/apps/viewer/src/opl-worklet.js +47 -0
- package/apps/viewer/src/opl-worklet.ts +55 -0
- package/apps/viewer/src/overlay.d.ts +31 -0
- package/apps/viewer/src/overlay.js +103 -0
- package/apps/viewer/src/overlay.ts +123 -0
- package/apps/viewer/src/play.css +14 -0
- package/apps/viewer/src/play.d.ts +1 -0
- package/apps/viewer/src/play.js +179 -0
- package/apps/viewer/src/play.ts +177 -0
- package/apps/viewer/src/saves.d.ts +10 -0
- package/apps/viewer/src/saves.js +50 -0
- package/apps/viewer/src/saves.ts +51 -0
- package/apps/viewer/src/session.d.ts +91 -0
- package/apps/viewer/src/session.js +301 -0
- package/apps/viewer/src/session.ts +321 -0
- package/apps/viewer/src/sound-preview.d.ts +7 -0
- package/apps/viewer/src/sound-preview.js +214 -0
- package/apps/viewer/src/sound-preview.ts +228 -0
- package/apps/viewer/src/style.css +64 -0
- package/apps/viewer/vite.config.ts +25 -0
- package/bin/sci-ts.mjs +9 -0
- package/lib/994.sc +49 -0
- package/lib/995.sc +53 -0
- package/lib/996.sc +299 -0
- package/lib/997.sc +198 -0
- package/lib/998.sc +338 -0
- package/lib/999.sc +189 -0
- package/lib/resources.d.ts +2 -0
- package/lib/resources.js +84 -0
- package/lib/resources.ts +88 -0
- package/lib/system.sh +62 -0
- package/package.json +75 -4
- package/packages/content/room.schema.json +267 -0
- package/packages/content/src/index.d.ts +3 -0
- package/packages/content/src/index.js +3 -0
- package/packages/content/src/index.ts +3 -0
- package/packages/content/src/room.d.ts +111 -0
- package/packages/content/src/room.js +886 -0
- package/packages/content/src/room.ts +1011 -0
- package/packages/content/src/target.d.ts +80 -0
- package/packages/content/src/target.js +1 -0
- package/packages/content/src/target.ts +60 -0
- package/packages/content/src/yarn.d.ts +90 -0
- package/packages/content/src/yarn.js +214 -0
- package/packages/content/src/yarn.ts +261 -0
- package/packages/sci/src/analysis/game.d.ts +90 -0
- package/packages/sci/src/analysis/game.js +221 -0
- package/packages/sci/src/analysis/game.ts +305 -0
- package/packages/sci/src/analysis/runtime.d.ts +58 -0
- package/packages/sci/src/analysis/runtime.js +133 -0
- package/packages/sci/src/analysis/runtime.ts +163 -0
- package/packages/sci/src/audio/adlib.d.ts +73 -0
- package/packages/sci/src/audio/adlib.js +280 -0
- package/packages/sci/src/audio/adlib.ts +329 -0
- package/packages/sci/src/audio/index.d.ts +43 -0
- package/packages/sci/src/audio/index.js +99 -0
- package/packages/sci/src/audio/index.ts +122 -0
- package/packages/sci/src/audio/midi.d.ts +92 -0
- package/packages/sci/src/audio/midi.js +242 -0
- package/packages/sci/src/audio/midi.ts +262 -0
- package/packages/sci/src/audio/opl2.d.ts +74 -0
- package/packages/sci/src/audio/opl2.js +283 -0
- package/packages/sci/src/audio/opl2.ts +283 -0
- package/packages/sci/src/audio/sol.d.ts +32 -0
- package/packages/sci/src/audio/sol.js +81 -0
- package/packages/sci/src/audio/sol.ts +100 -0
- package/packages/sci/src/audio/write.d.ts +35 -0
- package/packages/sci/src/audio/write.js +91 -0
- package/packages/sci/src/audio/write.ts +108 -0
- package/packages/sci/src/gfx/cel.d.ts +36 -0
- package/packages/sci/src/gfx/cel.js +82 -0
- package/packages/sci/src/gfx/cel.ts +110 -0
- package/packages/sci/src/gfx/palette.d.ts +24 -0
- package/packages/sci/src/gfx/palette.js +49 -0
- package/packages/sci/src/gfx/palette.ts +56 -0
- package/packages/sci/src/gfx/pic-file.d.ts +46 -0
- package/packages/sci/src/gfx/pic-file.js +114 -0
- package/packages/sci/src/gfx/pic-file.ts +155 -0
- package/packages/sci/src/gfx/pic.d.ts +27 -0
- package/packages/sci/src/gfx/pic.js +34 -0
- package/packages/sci/src/gfx/pic.ts +53 -0
- package/packages/sci/src/gfx/quantize.d.ts +42 -0
- package/packages/sci/src/gfx/quantize.js +199 -0
- package/packages/sci/src/gfx/quantize.ts +218 -0
- package/packages/sci/src/gfx/view-file.d.ts +62 -0
- package/packages/sci/src/gfx/view-file.js +182 -0
- package/packages/sci/src/gfx/view-file.ts +233 -0
- package/packages/sci/src/gfx/view.d.ts +28 -0
- package/packages/sci/src/gfx/view.js +45 -0
- package/packages/sci/src/gfx/view.ts +62 -0
- package/packages/sci/src/index.d.ts +38 -0
- package/packages/sci/src/index.js +38 -0
- package/packages/sci/src/index.ts +38 -0
- package/packages/sci/src/motion/pathfinding.d.ts +40 -0
- package/packages/sci/src/motion/pathfinding.js +140 -0
- package/packages/sci/src/motion/pathfinding.ts +147 -0
- package/packages/sci/src/resource/archive.d.ts +16 -0
- package/packages/sci/src/resource/archive.js +51 -0
- package/packages/sci/src/resource/archive.ts +56 -0
- package/packages/sci/src/resource/lzs.d.ts +12 -0
- package/packages/sci/src/resource/lzs.js +64 -0
- package/packages/sci/src/resource/lzs.ts +66 -0
- package/packages/sci/src/resource/manager.d.ts +45 -0
- package/packages/sci/src/resource/manager.js +158 -0
- package/packages/sci/src/resource/manager.ts +184 -0
- package/packages/sci/src/resource/map.d.ts +18 -0
- package/packages/sci/src/resource/map.js +34 -0
- package/packages/sci/src/resource/map.ts +44 -0
- package/packages/sci/src/resource/types.d.ts +37 -0
- package/packages/sci/src/resource/types.js +45 -0
- package/packages/sci/src/resource/types.ts +57 -0
- package/packages/sci/src/script/assembly.d.ts +67 -0
- package/packages/sci/src/script/assembly.js +748 -0
- package/packages/sci/src/script/assembly.ts +714 -0
- package/packages/sci/src/script/compiler.d.ts +75 -0
- package/packages/sci/src/script/compiler.js +993 -0
- package/packages/sci/src/script/compiler.ts +1040 -0
- package/packages/sci/src/script/disasm.d.ts +81 -0
- package/packages/sci/src/script/disasm.js +318 -0
- package/packages/sci/src/script/disasm.ts +323 -0
- package/packages/sci/src/script/kernel-names.d.ts +6 -0
- package/packages/sci/src/script/kernel-names.js +165 -0
- package/packages/sci/src/script/kernel-names.ts +165 -0
- package/packages/sci/src/script/opcodes.d.ts +22 -0
- package/packages/sci/src/script/opcodes.js +45 -0
- package/packages/sci/src/script/opcodes.ts +72 -0
- package/packages/sci/src/script/script.d.ts +56 -0
- package/packages/sci/src/script/script.js +63 -0
- package/packages/sci/src/script/script.ts +111 -0
- package/packages/sci/src/script/sexpr.d.ts +45 -0
- package/packages/sci/src/script/sexpr.js +151 -0
- package/packages/sci/src/script/sexpr.ts +139 -0
- package/packages/sci/src/script/vocab.d.ts +15 -0
- package/packages/sci/src/script/vocab.js +44 -0
- package/packages/sci/src/script/vocab.ts +48 -0
- package/packages/sci/src/script/world.d.ts +25 -0
- package/packages/sci/src/script/world.js +65 -0
- package/packages/sci/src/script/world.ts +70 -0
- package/packages/sci/src/text/font.d.ts +37 -0
- package/packages/sci/src/text/font.js +103 -0
- package/packages/sci/src/text/font.ts +125 -0
- package/packages/sci/src/text/message.d.ts +69 -0
- package/packages/sci/src/text/message.js +171 -0
- package/packages/sci/src/text/message.ts +205 -0
- package/packages/sci/src/vm/kernels/arrays.d.ts +19 -0
- package/packages/sci/src/vm/kernels/arrays.js +182 -0
- package/packages/sci/src/vm/kernels/arrays.ts +164 -0
- package/packages/sci/src/vm/kernels/audio.d.ts +70 -0
- package/packages/sci/src/vm/kernels/audio.js +347 -0
- package/packages/sci/src/vm/kernels/audio.ts +349 -0
- package/packages/sci/src/vm/kernels/core.d.ts +5 -0
- package/packages/sci/src/vm/kernels/core.js +87 -0
- package/packages/sci/src/vm/kernels/core.ts +91 -0
- package/packages/sci/src/vm/kernels/graphics.d.ts +141 -0
- package/packages/sci/src/vm/kernels/graphics.js +467 -0
- package/packages/sci/src/vm/kernels/graphics.ts +502 -0
- package/packages/sci/src/vm/kernels/index.d.ts +8 -0
- package/packages/sci/src/vm/kernels/index.js +29 -0
- package/packages/sci/src/vm/kernels/index.ts +32 -0
- package/packages/sci/src/vm/kernels/lists.d.ts +6 -0
- package/packages/sci/src/vm/kernels/lists.js +127 -0
- package/packages/sci/src/vm/kernels/lists.ts +111 -0
- package/packages/sci/src/vm/kernels/motion.d.ts +7 -0
- package/packages/sci/src/vm/kernels/motion.js +204 -0
- package/packages/sci/src/vm/kernels/motion.ts +194 -0
- package/packages/sci/src/vm/kernels/palette.d.ts +82 -0
- package/packages/sci/src/vm/kernels/palette.js +292 -0
- package/packages/sci/src/vm/kernels/palette.ts +303 -0
- package/packages/sci/src/vm/kernels/saves.d.ts +45 -0
- package/packages/sci/src/vm/kernels/saves.js +123 -0
- package/packages/sci/src/vm/kernels/saves.ts +149 -0
- package/packages/sci/src/vm/kernels/system.d.ts +62 -0
- package/packages/sci/src/vm/kernels/system.js +110 -0
- package/packages/sci/src/vm/kernels/system.ts +128 -0
- package/packages/sci/src/vm/kernels/text.d.ts +13 -0
- package/packages/sci/src/vm/kernels/text.js +338 -0
- package/packages/sci/src/vm/kernels/text.ts +328 -0
- package/packages/sci/src/vm/memory.d.ts +138 -0
- package/packages/sci/src/vm/memory.js +168 -0
- package/packages/sci/src/vm/memory.ts +266 -0
- package/packages/sci/src/vm/replace.d.ts +11 -0
- package/packages/sci/src/vm/replace.js +14 -0
- package/packages/sci/src/vm/replace.ts +16 -0
- package/packages/sci/src/vm/savegame.d.ts +89 -0
- package/packages/sci/src/vm/savegame.js +161 -0
- package/packages/sci/src/vm/savegame.ts +188 -0
- package/packages/sci/src/vm/value.d.ts +17 -0
- package/packages/sci/src/vm/value.js +12 -0
- package/packages/sci/src/vm/value.ts +25 -0
- package/packages/sci/src/vm/vm.d.ts +155 -0
- package/packages/sci/src/vm/vm.js +1053 -0
- package/packages/sci/src/vm/vm.ts +914 -0
- package/tools/adlib-render.d.ts +1 -0
- package/tools/adlib-render.js +49 -0
- package/tools/adlib-render.ts +42 -0
- package/tools/analyze.d.ts +1 -0
- package/tools/analyze.js +25 -0
- package/tools/analyze.ts +25 -0
- package/tools/art/build.d.ts +34 -0
- package/tools/art/build.js +175 -0
- package/tools/art/build.ts +153 -0
- package/tools/art.d.ts +1 -0
- package/tools/art.js +31 -0
- package/tools/art.ts +29 -0
- package/tools/asm.d.ts +1 -0
- package/tools/asm.js +61 -0
- package/tools/asm.ts +55 -0
- package/tools/cli.d.ts +1 -0
- package/tools/cli.js +99 -0
- package/tools/cli.ts +100 -0
- package/tools/editor-server.d.ts +29 -0
- package/tools/editor-server.js +162 -0
- package/tools/editor-server.ts +176 -0
- package/tools/explore.d.ts +1 -0
- package/tools/explore.js +305 -0
- package/tools/explore.ts +250 -0
- package/tools/game/audio.d.ts +19 -0
- package/tools/game/audio.js +143 -0
- package/tools/game/audio.ts +130 -0
- package/tools/game/build.d.ts +46 -0
- package/tools/game/build.js +259 -0
- package/tools/game/build.ts +283 -0
- package/tools/game/defaults.d.ts +26 -0
- package/tools/game/defaults.js +82 -0
- package/tools/game/defaults.ts +83 -0
- package/tools/game/font.d.ts +3 -0
- package/tools/game/font.js +124 -0
- package/tools/game/font.ts +126 -0
- package/tools/game/kit.d.ts +7 -0
- package/tools/game/kit.js +7 -0
- package/tools/game/kit.ts +10 -0
- package/tools/game/new.d.ts +8 -0
- package/tools/game/new.js +39 -0
- package/tools/game/new.ts +38 -0
- package/tools/game/target.d.ts +7 -0
- package/tools/game/target.js +41 -0
- package/tools/game/target.ts +42 -0
- package/tools/game/template/resources.ts.tmpl +43 -0
- package/tools/game/template/rooms/1.room.yaml +9 -0
- package/tools/game/template/rooms/1.yarn +17 -0
- package/tools/game/template/scripts/0.sc +24 -0
- package/tools/game.d.ts +1 -0
- package/tools/game.js +77 -0
- package/tools/game.ts +70 -0
- package/tools/mod-build.d.ts +28 -0
- package/tools/mod-build.js +173 -0
- package/tools/mod-build.ts +179 -0
- package/tools/mod-target.d.ts +16 -0
- package/tools/mod-target.js +26 -0
- package/tools/mod-target.ts +32 -0
- package/tools/mod.d.ts +1 -0
- package/tools/mod.js +47 -0
- package/tools/mod.ts +45 -0
- package/tools/msg.d.ts +1 -0
- package/tools/msg.js +40 -0
- package/tools/msg.ts +36 -0
- package/tools/node-files.d.ts +3 -0
- package/tools/node-files.js +34 -0
- package/tools/node-files.ts +33 -0
- package/tools/patches.d.ts +3 -0
- package/tools/patches.js +13 -0
- package/tools/patches.ts +13 -0
- package/tools/pic-build.d.ts +9 -0
- package/tools/pic-build.js +26 -0
- package/tools/pic-build.ts +27 -0
- package/tools/pic.d.ts +1 -0
- package/tools/pic.js +64 -0
- package/tools/pic.ts +61 -0
- package/tools/play.d.ts +1 -0
- package/tools/play.js +54 -0
- package/tools/play.ts +54 -0
- package/tools/png.d.ts +16 -0
- package/tools/png.js +124 -0
- package/tools/png.ts +123 -0
- package/tools/res.d.ts +1 -0
- package/tools/res.js +48 -0
- package/tools/res.ts +50 -0
- package/tools/scenario.d.ts +1 -0
- package/tools/scenario.js +74 -0
- package/tools/scenario.ts +80 -0
- package/tools/site/build.d.ts +8 -0
- package/tools/site/build.js +110 -0
- package/tools/site/build.ts +116 -0
- package/tools/site/markdown.d.ts +11 -0
- package/tools/site/markdown.js +88 -0
- package/tools/site/markdown.ts +84 -0
- package/tools/view-build.d.ts +34 -0
- package/tools/view-build.js +47 -0
- package/tools/view-build.ts +71 -0
- package/tools/view.d.ts +1 -0
- package/tools/view.js +54 -0
- package/tools/view.ts +52 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { type Script, type ScriptObject } from "./script.ts";
|
|
2
|
+
export interface Instruction {
|
|
3
|
+
offset: number;
|
|
4
|
+
length: number;
|
|
5
|
+
opcode: number;
|
|
6
|
+
name: string;
|
|
7
|
+
operands: number[];
|
|
8
|
+
/** Absolute target for jumps and local calls. */
|
|
9
|
+
target?: number;
|
|
10
|
+
}
|
|
11
|
+
/** Decodes one instruction at `offset` in the script's code. */
|
|
12
|
+
export declare function decodeInstruction(code: Uint8Array, offset: number): Instruction;
|
|
13
|
+
/**
|
|
14
|
+
* Traces reachable code from `entry`, following both sides of branches, until every path
|
|
15
|
+
* hits `ret`. Local `call` targets are collected, not followed: they're separate functions.
|
|
16
|
+
*/
|
|
17
|
+
export declare function traceFunction(code: Uint8Array, entry: number): {
|
|
18
|
+
instructions: Instruction[];
|
|
19
|
+
calls: number[];
|
|
20
|
+
};
|
|
21
|
+
/** Names the disassembler can't know from one script alone. */
|
|
22
|
+
export interface DisasmContext {
|
|
23
|
+
selectorNames: readonly string[];
|
|
24
|
+
kernelNames: readonly string[];
|
|
25
|
+
/** Class number → class name. */
|
|
26
|
+
className(species: number): string | undefined;
|
|
27
|
+
/** Property selectors for an object (its own if a class, else its class's). */
|
|
28
|
+
propSelectors(obj: ScriptObject): readonly number[] | undefined;
|
|
29
|
+
}
|
|
30
|
+
export interface DisasmFunction {
|
|
31
|
+
label: string;
|
|
32
|
+
entry: number;
|
|
33
|
+
owner?: ScriptObject;
|
|
34
|
+
instructions: Instruction[];
|
|
35
|
+
/** Per-instruction comment, keyed by offset. */
|
|
36
|
+
comments: Map<number, string>;
|
|
37
|
+
/** What the linear stack simulation could tell about each send, call and comparison. */
|
|
38
|
+
facts: FunctionFacts;
|
|
39
|
+
}
|
|
40
|
+
/** A value as far as one linear pass can tell: a constant, an object/string, a variable. */
|
|
41
|
+
export interface ValueFact {
|
|
42
|
+
const?: number;
|
|
43
|
+
/** Heap offset of an object or string in this script (lofsa/lofss). */
|
|
44
|
+
heap?: number;
|
|
45
|
+
global?: number;
|
|
46
|
+
/** Property of the method's own object, by selector. */
|
|
47
|
+
prop?: number;
|
|
48
|
+
self?: true;
|
|
49
|
+
}
|
|
50
|
+
export interface SendFact {
|
|
51
|
+
offset: number;
|
|
52
|
+
/** `send` goes to whatever acc holds; `self`/`super` to the method's object. */
|
|
53
|
+
receiver: ValueFact;
|
|
54
|
+
selector: number;
|
|
55
|
+
args: ValueFact[];
|
|
56
|
+
}
|
|
57
|
+
export interface CallFact {
|
|
58
|
+
offset: number;
|
|
59
|
+
kind: "kernel" | "export";
|
|
60
|
+
/** Kernel number, or the script number for an export call. */
|
|
61
|
+
target: number;
|
|
62
|
+
/** Export index for export calls. */
|
|
63
|
+
index?: number;
|
|
64
|
+
args: ValueFact[];
|
|
65
|
+
}
|
|
66
|
+
/** `global <op> constant`, e.g. switch cases over the previous room number. */
|
|
67
|
+
export interface CompareFact {
|
|
68
|
+
offset: number;
|
|
69
|
+
global: number;
|
|
70
|
+
op: string;
|
|
71
|
+
value: number;
|
|
72
|
+
}
|
|
73
|
+
export interface FunctionFacts {
|
|
74
|
+
sends: SendFact[];
|
|
75
|
+
calls: CallFact[];
|
|
76
|
+
compares: CompareFact[];
|
|
77
|
+
}
|
|
78
|
+
/** Disassembles every method and exported/called procedure in a script. */
|
|
79
|
+
export declare function disassemble(script: Script, ctx: DisasmContext): DisasmFunction[];
|
|
80
|
+
/** Plain-text listing, one function per block. */
|
|
81
|
+
export declare function formatDisassembly(script: Script, functions: DisasmFunction[]): string;
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { opcodes, varKind } from "./opcodes.js";
|
|
2
|
+
import { readString } from "./script.js";
|
|
3
|
+
/** Decodes one instruction at `offset` in the script's code. */
|
|
4
|
+
export function decodeInstruction(code, offset) {
|
|
5
|
+
const v = new DataView(code.buffer, code.byteOffset, code.byteLength);
|
|
6
|
+
const raw = code[offset];
|
|
7
|
+
const opcode = raw >> 1;
|
|
8
|
+
const small = (raw & 1) === 1;
|
|
9
|
+
const info = opcodes[opcode];
|
|
10
|
+
let pos = offset + 1;
|
|
11
|
+
const operands = [];
|
|
12
|
+
for (const kind of info.operands) {
|
|
13
|
+
if (kind === "B") {
|
|
14
|
+
operands.push(code[pos++]);
|
|
15
|
+
}
|
|
16
|
+
else if (kind === "U") {
|
|
17
|
+
operands.push(v.getUint16(pos, true));
|
|
18
|
+
pos += 2;
|
|
19
|
+
}
|
|
20
|
+
else if (small) {
|
|
21
|
+
operands.push(kind === "W" ? code[pos] : v.getInt8(pos));
|
|
22
|
+
pos += 1;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
operands.push(kind === "W" ? v.getUint16(pos, true) : v.getInt16(pos, true));
|
|
26
|
+
pos += 2;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const ins = { offset, length: pos - offset, opcode, name: info.name, operands };
|
|
30
|
+
if (info.operands[0] === "R")
|
|
31
|
+
ins.target = pos + operands[0];
|
|
32
|
+
return ins;
|
|
33
|
+
}
|
|
34
|
+
const RET = 0x24;
|
|
35
|
+
const JMP = 0x19;
|
|
36
|
+
const CALL = 0x20;
|
|
37
|
+
/**
|
|
38
|
+
* Traces reachable code from `entry`, following both sides of branches, until every path
|
|
39
|
+
* hits `ret`. Local `call` targets are collected, not followed: they're separate functions.
|
|
40
|
+
*/
|
|
41
|
+
export function traceFunction(code, entry) {
|
|
42
|
+
const seen = new Map();
|
|
43
|
+
const calls = new Set();
|
|
44
|
+
const work = [entry];
|
|
45
|
+
while (work.length) {
|
|
46
|
+
let pc = work.pop();
|
|
47
|
+
while (!seen.has(pc) && pc >= 0 && pc < code.length) {
|
|
48
|
+
const ins = decodeInstruction(code, pc);
|
|
49
|
+
seen.set(pc, ins);
|
|
50
|
+
if (ins.opcode === CALL)
|
|
51
|
+
calls.add(ins.target);
|
|
52
|
+
else if (ins.target !== undefined)
|
|
53
|
+
work.push(ins.target);
|
|
54
|
+
if (ins.opcode === RET || ins.opcode === JMP)
|
|
55
|
+
break;
|
|
56
|
+
pc += ins.length;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return { instructions: [...seen.values()].sort((a, b) => a.offset - b.offset), calls: [...calls] };
|
|
60
|
+
}
|
|
61
|
+
const hex = (n, w = 4) => n.toString(16).padStart(w, "0");
|
|
62
|
+
/** Disassembles every method and exported/called procedure in a script. */
|
|
63
|
+
export function disassemble(script, ctx) {
|
|
64
|
+
const functions = new Map();
|
|
65
|
+
const pending = [];
|
|
66
|
+
for (const obj of script.objects) {
|
|
67
|
+
for (const m of obj.methods) {
|
|
68
|
+
pending.push({ entry: m.offset, label: `${obj.name}::${ctx.selectorNames[m.selector] ?? `sel_${m.selector}`}`, owner: obj });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
for (const e of script.exports) {
|
|
72
|
+
if (e.kind === "code")
|
|
73
|
+
pending.push({ entry: e.offset, label: `export${e.index}` });
|
|
74
|
+
}
|
|
75
|
+
while (pending.length) {
|
|
76
|
+
const { entry, label, owner } = pending.shift();
|
|
77
|
+
if (functions.has(entry))
|
|
78
|
+
continue;
|
|
79
|
+
const { instructions, calls } = traceFunction(script.code, entry);
|
|
80
|
+
const fn = { label, entry, owner, instructions, comments: new Map(), facts: { sends: [], calls: [], compares: [] } };
|
|
81
|
+
functions.set(entry, fn);
|
|
82
|
+
// Local procedures inherit the caller's object context for property names.
|
|
83
|
+
for (const c of calls)
|
|
84
|
+
if (!functions.has(c))
|
|
85
|
+
pending.push({ entry: c, label: `proc_${hex(c)}`, owner });
|
|
86
|
+
}
|
|
87
|
+
const labels = new Map([...functions.values()].map((f) => [f.entry, f.label]));
|
|
88
|
+
for (const fn of functions.values())
|
|
89
|
+
annotate(fn, script, ctx, labels);
|
|
90
|
+
return [...functions.values()].sort((a, b) => a.entry - b.entry);
|
|
91
|
+
}
|
|
92
|
+
/** Globals whose meaning is fixed by SCI's system scripts. */
|
|
93
|
+
const wellKnownGlobals = { 0: "ego", 1: "theGame" };
|
|
94
|
+
const COMPARISONS = new Set(["eq?", "ne?", "gt?", "ge?", "lt?", "le?", "ugt?", "uge?", "ult?", "ule?"]);
|
|
95
|
+
const valueOf = ({ offset: _, ...v }) => v;
|
|
96
|
+
function annotate(fn, script, ctx, labels) {
|
|
97
|
+
const { comments } = fn;
|
|
98
|
+
const sel = (n) => ctx.selectorNames[n] ?? `sel_${n}`;
|
|
99
|
+
const props = fn.owner && ctx.propSelectors(fn.owner);
|
|
100
|
+
const heapLabel = (o) => {
|
|
101
|
+
const obj = script.objects.find((x) => x.offset === o);
|
|
102
|
+
if (obj)
|
|
103
|
+
return obj.name;
|
|
104
|
+
const s = readString(script.heap, o);
|
|
105
|
+
return s ? JSON.stringify(s.length > 60 ? `${s.slice(0, 57)}...` : s) : undefined;
|
|
106
|
+
};
|
|
107
|
+
const { facts } = fn;
|
|
108
|
+
let stack = [];
|
|
109
|
+
let acc = {};
|
|
110
|
+
const push = (ins, value, v = {}) => stack.push({ ...v, ...(value === undefined ? {} : { const: value }), offset: ins.offset });
|
|
111
|
+
const pop = (n) => stack.splice(Math.max(0, stack.length - n), n);
|
|
112
|
+
/** Arguments of a call frame: [argc, ...args]. */
|
|
113
|
+
const callArgs = (frame) => frame.slice(1).map(valueOf);
|
|
114
|
+
/** Splits a send frame into `selector(args)` messages; skips it if the frame doesn't parse. */
|
|
115
|
+
const describeSend = (frame, receiver, offset) => {
|
|
116
|
+
const parts = [];
|
|
117
|
+
const sends = [];
|
|
118
|
+
for (let i = 0; i < frame.length;) {
|
|
119
|
+
const s = frame[i]?.const;
|
|
120
|
+
const argc = frame[i + 1]?.const;
|
|
121
|
+
if (s === undefined || argc === undefined || i + 2 + argc > frame.length)
|
|
122
|
+
return undefined;
|
|
123
|
+
comments.set(frame[i].offset, sel(s));
|
|
124
|
+
parts.push(`${sel(s)}${argc ? `(${argc} arg${argc > 1 ? "s" : ""})` : ""}`);
|
|
125
|
+
sends.push({ offset, receiver, selector: s, args: frame.slice(i + 2, i + 2 + argc).map(valueOf) });
|
|
126
|
+
i += 2 + argc;
|
|
127
|
+
}
|
|
128
|
+
facts.sends.push(...sends);
|
|
129
|
+
return parts.join(", ");
|
|
130
|
+
};
|
|
131
|
+
for (const ins of fn.instructions) {
|
|
132
|
+
const [a = 0, b = 0] = ins.operands;
|
|
133
|
+
const o = ins.opcode;
|
|
134
|
+
if (labels.has(ins.offset) && ins.offset !== fn.entry) {
|
|
135
|
+
// A jump target: several paths meet here, so forget acc. Compiled code keeps the stack
|
|
136
|
+
// balanced across branches (switch statements keep their subject on it), so keep that.
|
|
137
|
+
acc = {};
|
|
138
|
+
}
|
|
139
|
+
const propSel = (offset) => props?.[offset / 2];
|
|
140
|
+
let nextAcc = {};
|
|
141
|
+
switch (ins.name) {
|
|
142
|
+
case "push":
|
|
143
|
+
push(ins, undefined, acc);
|
|
144
|
+
break;
|
|
145
|
+
case "dup": {
|
|
146
|
+
const top = stack.at(-1);
|
|
147
|
+
push(ins, undefined, top ? valueOf(top) : {});
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case "pushSelf":
|
|
151
|
+
push(ins, undefined, { self: true });
|
|
152
|
+
break;
|
|
153
|
+
case "pTos": {
|
|
154
|
+
const s = propSel(a);
|
|
155
|
+
push(ins, undefined, s === undefined ? {} : { prop: s });
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
case "ipTos":
|
|
159
|
+
case "dpTos":
|
|
160
|
+
push(ins);
|
|
161
|
+
break;
|
|
162
|
+
case "pushi":
|
|
163
|
+
push(ins, a);
|
|
164
|
+
break;
|
|
165
|
+
case "push0":
|
|
166
|
+
push(ins, 0);
|
|
167
|
+
break;
|
|
168
|
+
case "push1":
|
|
169
|
+
push(ins, 1);
|
|
170
|
+
break;
|
|
171
|
+
case "push2":
|
|
172
|
+
push(ins, 2);
|
|
173
|
+
break;
|
|
174
|
+
case "lofss":
|
|
175
|
+
push(ins, undefined, { heap: a });
|
|
176
|
+
break;
|
|
177
|
+
case "toss":
|
|
178
|
+
pop(1);
|
|
179
|
+
break;
|
|
180
|
+
case "&rest":
|
|
181
|
+
stack = [];
|
|
182
|
+
break; // pushes an unknown number of values
|
|
183
|
+
case "ldi":
|
|
184
|
+
nextAcc = { const: a };
|
|
185
|
+
break;
|
|
186
|
+
case "lofsa":
|
|
187
|
+
nextAcc = { heap: a };
|
|
188
|
+
break;
|
|
189
|
+
case "selfID":
|
|
190
|
+
nextAcc = { self: true };
|
|
191
|
+
break;
|
|
192
|
+
case "pToa": {
|
|
193
|
+
const s = propSel(a);
|
|
194
|
+
if (s !== undefined)
|
|
195
|
+
nextAcc = { prop: s };
|
|
196
|
+
break;
|
|
197
|
+
}
|
|
198
|
+
case "call":
|
|
199
|
+
pop(ins.operands.at(-1) / 2 + 1);
|
|
200
|
+
break;
|
|
201
|
+
case "callk":
|
|
202
|
+
case "callb":
|
|
203
|
+
case "calle": {
|
|
204
|
+
const frame = pop(ins.operands.at(-1) / 2 + 1);
|
|
205
|
+
const call = ins.name === "callk"
|
|
206
|
+
? { offset: ins.offset, kind: "kernel", target: a, args: callArgs(frame) }
|
|
207
|
+
: ins.name === "callb"
|
|
208
|
+
? { offset: ins.offset, kind: "export", target: 0, index: a, args: callArgs(frame) }
|
|
209
|
+
: { offset: ins.offset, kind: "export", target: a, index: b, args: callArgs(frame) };
|
|
210
|
+
facts.calls.push(call);
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
case "send":
|
|
214
|
+
case "self":
|
|
215
|
+
case "super": {
|
|
216
|
+
const frame = pop(ins.operands.at(-1) / 2);
|
|
217
|
+
const receiver = ins.name === "send" ? acc : { self: true };
|
|
218
|
+
const msg = describeSend(frame, receiver, ins.offset);
|
|
219
|
+
const who = ins.name === "super" ? `super ${ctx.className(a) ?? a}` : ins.name === "self" ? "self" : "acc";
|
|
220
|
+
if (msg)
|
|
221
|
+
comments.set(ins.offset, `${who}: ${msg}`);
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
default:
|
|
225
|
+
if ((o >= 0x01 && o <= 0x0a) || (o >= 0x0d && o <= 0x16)) {
|
|
226
|
+
// Binary ops: stack top <op> acc.
|
|
227
|
+
const [left] = pop(1);
|
|
228
|
+
if (COMPARISONS.has(ins.name) && left?.global !== undefined && acc.const !== undefined) {
|
|
229
|
+
facts.compares.push({ offset: ins.offset, global: left.global, op: ins.name, value: acc.const });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
else if (o === 0x17 || o === 0x18) { /* branches read acc */
|
|
233
|
+
nextAcc = acc;
|
|
234
|
+
}
|
|
235
|
+
else if (o >= 0x40) {
|
|
236
|
+
const action = o >> 4; // 4 load, 5 store, 6 inc, 7 dec
|
|
237
|
+
const toStack = (o & 4) !== 0;
|
|
238
|
+
const indexed = (o & 8) !== 0;
|
|
239
|
+
const v = varKind(o) === "global" && !indexed ? { global: a } : {};
|
|
240
|
+
if (action === 5 && toStack)
|
|
241
|
+
pop(1);
|
|
242
|
+
else if (action !== 5 && toStack)
|
|
243
|
+
push(ins, undefined, action === 4 ? v : {});
|
|
244
|
+
else if (action === 4)
|
|
245
|
+
nextAcc = v;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
acc = nextAcc;
|
|
249
|
+
// Operand names
|
|
250
|
+
switch (ins.name) {
|
|
251
|
+
case "callk":
|
|
252
|
+
comments.set(ins.offset, `${ctx.kernelNames[a] ?? `kernel_${a}`}`);
|
|
253
|
+
break;
|
|
254
|
+
case "call":
|
|
255
|
+
comments.set(ins.offset, labels.get(ins.target) ?? `proc_${hex(ins.target)}`);
|
|
256
|
+
break;
|
|
257
|
+
case "callb":
|
|
258
|
+
comments.set(ins.offset, `script 0 export ${a}`);
|
|
259
|
+
break;
|
|
260
|
+
case "calle":
|
|
261
|
+
comments.set(ins.offset, `script ${a} export ${b}`);
|
|
262
|
+
break;
|
|
263
|
+
case "class":
|
|
264
|
+
comments.set(ins.offset, ctx.className(a) ?? `class ${a}`);
|
|
265
|
+
break;
|
|
266
|
+
case "lofsa":
|
|
267
|
+
case "lofss": {
|
|
268
|
+
const l = heapLabel(a);
|
|
269
|
+
if (l)
|
|
270
|
+
comments.set(ins.offset, l);
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
case "pToa":
|
|
274
|
+
case "aTop":
|
|
275
|
+
case "pTos":
|
|
276
|
+
case "sTop":
|
|
277
|
+
case "ipToa":
|
|
278
|
+
case "dpToa":
|
|
279
|
+
case "ipTos":
|
|
280
|
+
case "dpTos": {
|
|
281
|
+
const s = props?.[a / 2];
|
|
282
|
+
if (s !== undefined)
|
|
283
|
+
comments.set(ins.offset, sel(s));
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (o >= 0x40) {
|
|
288
|
+
const kind = varKind(o);
|
|
289
|
+
const name = kind === "global" && wellKnownGlobals[a] ? wellKnownGlobals[a] : `${kind}${a}`;
|
|
290
|
+
if (!comments.has(ins.offset))
|
|
291
|
+
comments.set(ins.offset, name);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/** Plain-text listing, one function per block. */
|
|
296
|
+
export function formatDisassembly(script, functions) {
|
|
297
|
+
const out = [`; script ${script.number}`];
|
|
298
|
+
for (const obj of script.objects) {
|
|
299
|
+
out.push(`; ${obj.isClass ? "class" : "instance"} ${obj.name} @heap ${hex(obj.offset)} (species ${obj.isClass ? obj.species : "-"}, super ${obj.superclass})`);
|
|
300
|
+
}
|
|
301
|
+
for (const e of script.exports)
|
|
302
|
+
out.push(`; export ${e.index}: ${e.kind} @${hex(e.offset)}`);
|
|
303
|
+
for (const fn of functions) {
|
|
304
|
+
const targets = new Set(fn.instructions.filter((i) => i.target !== undefined && i.name !== "call").map((i) => i.target));
|
|
305
|
+
out.push("", `(${fn.label})`);
|
|
306
|
+
for (const ins of fn.instructions) {
|
|
307
|
+
if (targets.has(ins.offset))
|
|
308
|
+
out.push(` code_${hex(ins.offset)}:`);
|
|
309
|
+
const bytes = [...script.code.subarray(ins.offset, ins.offset + ins.length)].map((b) => hex(b, 2)).join(" ");
|
|
310
|
+
const args = ins.target !== undefined
|
|
311
|
+
? [ins.name === "call" ? `proc_${hex(ins.target)}` : `code_${hex(ins.target)}`, ...ins.operands.slice(1)]
|
|
312
|
+
: ins.operands.map((n) => (n > 9 ? `$${hex(n)}` : String(n)));
|
|
313
|
+
const comment = fn.comments.get(ins.offset);
|
|
314
|
+
out.push(` ${hex(ins.offset)}: ${bytes.padEnd(18)} ${ins.name.padEnd(8)} ${args.join(" ").padEnd(16)}${comment ? `; ${comment}` : ""}`.trimEnd());
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return out.join("\n");
|
|
318
|
+
}
|