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,323 @@
|
|
|
1
|
+
import { opcodes, varKind } from "./opcodes.ts";
|
|
2
|
+
import { readString, type Script, type ScriptObject } from "./script.ts";
|
|
3
|
+
|
|
4
|
+
export interface Instruction {
|
|
5
|
+
offset: number;
|
|
6
|
+
length: number;
|
|
7
|
+
opcode: number;
|
|
8
|
+
name: string;
|
|
9
|
+
operands: number[];
|
|
10
|
+
/** Absolute target for jumps and local calls. */
|
|
11
|
+
target?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Decodes one instruction at `offset` in the script's code. */
|
|
15
|
+
export function decodeInstruction(code: Uint8Array, offset: number): Instruction {
|
|
16
|
+
const v = new DataView(code.buffer, code.byteOffset, code.byteLength);
|
|
17
|
+
const raw = code[offset]!;
|
|
18
|
+
const opcode = raw >> 1;
|
|
19
|
+
const small = (raw & 1) === 1;
|
|
20
|
+
const info = opcodes[opcode]!;
|
|
21
|
+
|
|
22
|
+
let pos = offset + 1;
|
|
23
|
+
const operands: number[] = [];
|
|
24
|
+
for (const kind of info.operands) {
|
|
25
|
+
if (kind === "B") {
|
|
26
|
+
operands.push(code[pos++]!);
|
|
27
|
+
} else if (kind === "U") {
|
|
28
|
+
operands.push(v.getUint16(pos, true));
|
|
29
|
+
pos += 2;
|
|
30
|
+
} else if (small) {
|
|
31
|
+
operands.push(kind === "W" ? code[pos]! : v.getInt8(pos));
|
|
32
|
+
pos += 1;
|
|
33
|
+
} else {
|
|
34
|
+
operands.push(kind === "W" ? v.getUint16(pos, true) : v.getInt16(pos, true));
|
|
35
|
+
pos += 2;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const ins: Instruction = { offset, length: pos - offset, opcode, name: info.name, operands };
|
|
40
|
+
if (info.operands[0] === "R") ins.target = pos + operands[0]!;
|
|
41
|
+
return ins;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const RET = 0x24;
|
|
45
|
+
const JMP = 0x19;
|
|
46
|
+
const CALL = 0x20;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Traces reachable code from `entry`, following both sides of branches, until every path
|
|
50
|
+
* hits `ret`. Local `call` targets are collected, not followed: they're separate functions.
|
|
51
|
+
*/
|
|
52
|
+
export function traceFunction(code: Uint8Array, entry: number): { instructions: Instruction[]; calls: number[] } {
|
|
53
|
+
const seen = new Map<number, Instruction>();
|
|
54
|
+
const calls = new Set<number>();
|
|
55
|
+
const work = [entry];
|
|
56
|
+
|
|
57
|
+
while (work.length) {
|
|
58
|
+
let pc = work.pop()!;
|
|
59
|
+
while (!seen.has(pc) && pc >= 0 && pc < code.length) {
|
|
60
|
+
const ins = decodeInstruction(code, pc);
|
|
61
|
+
seen.set(pc, ins);
|
|
62
|
+
if (ins.opcode === CALL) calls.add(ins.target!);
|
|
63
|
+
else if (ins.target !== undefined) work.push(ins.target);
|
|
64
|
+
if (ins.opcode === RET || ins.opcode === JMP) break;
|
|
65
|
+
pc += ins.length;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { instructions: [...seen.values()].sort((a, b) => a.offset - b.offset), calls: [...calls] };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Names the disassembler can't know from one script alone. */
|
|
72
|
+
export interface DisasmContext {
|
|
73
|
+
selectorNames: readonly string[];
|
|
74
|
+
kernelNames: readonly string[];
|
|
75
|
+
/** Class number → class name. */
|
|
76
|
+
className(species: number): string | undefined;
|
|
77
|
+
/** Property selectors for an object (its own if a class, else its class's). */
|
|
78
|
+
propSelectors(obj: ScriptObject): readonly number[] | undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface DisasmFunction {
|
|
82
|
+
label: string;
|
|
83
|
+
entry: number;
|
|
84
|
+
owner?: ScriptObject;
|
|
85
|
+
instructions: Instruction[];
|
|
86
|
+
/** Per-instruction comment, keyed by offset. */
|
|
87
|
+
comments: Map<number, string>;
|
|
88
|
+
/** What the linear stack simulation could tell about each send, call and comparison. */
|
|
89
|
+
facts: FunctionFacts;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** A value as far as one linear pass can tell: a constant, an object/string, a variable. */
|
|
93
|
+
export interface ValueFact {
|
|
94
|
+
const?: number;
|
|
95
|
+
/** Heap offset of an object or string in this script (lofsa/lofss). */
|
|
96
|
+
heap?: number;
|
|
97
|
+
global?: number;
|
|
98
|
+
/** Property of the method's own object, by selector. */
|
|
99
|
+
prop?: number;
|
|
100
|
+
self?: true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface SendFact {
|
|
104
|
+
offset: number;
|
|
105
|
+
/** `send` goes to whatever acc holds; `self`/`super` to the method's object. */
|
|
106
|
+
receiver: ValueFact;
|
|
107
|
+
selector: number;
|
|
108
|
+
args: ValueFact[];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface CallFact {
|
|
112
|
+
offset: number;
|
|
113
|
+
kind: "kernel" | "export";
|
|
114
|
+
/** Kernel number, or the script number for an export call. */
|
|
115
|
+
target: number;
|
|
116
|
+
/** Export index for export calls. */
|
|
117
|
+
index?: number;
|
|
118
|
+
args: ValueFact[];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** `global <op> constant`, e.g. switch cases over the previous room number. */
|
|
122
|
+
export interface CompareFact {
|
|
123
|
+
offset: number;
|
|
124
|
+
global: number;
|
|
125
|
+
op: string;
|
|
126
|
+
value: number;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface FunctionFacts {
|
|
130
|
+
sends: SendFact[];
|
|
131
|
+
calls: CallFact[];
|
|
132
|
+
compares: CompareFact[];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const hex = (n: number, w = 4) => n.toString(16).padStart(w, "0");
|
|
136
|
+
|
|
137
|
+
/** Disassembles every method and exported/called procedure in a script. */
|
|
138
|
+
export function disassemble(script: Script, ctx: DisasmContext): DisasmFunction[] {
|
|
139
|
+
const functions = new Map<number, DisasmFunction>();
|
|
140
|
+
const pending: { entry: number; label: string; owner?: ScriptObject }[] = [];
|
|
141
|
+
|
|
142
|
+
for (const obj of script.objects) {
|
|
143
|
+
for (const m of obj.methods) {
|
|
144
|
+
pending.push({ entry: m.offset, label: `${obj.name}::${ctx.selectorNames[m.selector] ?? `sel_${m.selector}`}`, owner: obj });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
for (const e of script.exports) {
|
|
148
|
+
if (e.kind === "code") pending.push({ entry: e.offset, label: `export${e.index}` });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
while (pending.length) {
|
|
152
|
+
const { entry, label, owner } = pending.shift()!;
|
|
153
|
+
if (functions.has(entry)) continue;
|
|
154
|
+
const { instructions, calls } = traceFunction(script.code, entry);
|
|
155
|
+
const fn: DisasmFunction = { label, entry, owner, instructions, comments: new Map(), facts: { sends: [], calls: [], compares: [] } };
|
|
156
|
+
functions.set(entry, fn);
|
|
157
|
+
// Local procedures inherit the caller's object context for property names.
|
|
158
|
+
for (const c of calls) if (!functions.has(c)) pending.push({ entry: c, label: `proc_${hex(c)}`, owner });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const labels = new Map([...functions.values()].map((f) => [f.entry, f.label]));
|
|
162
|
+
for (const fn of functions.values()) annotate(fn, script, ctx, labels);
|
|
163
|
+
return [...functions.values()].sort((a, b) => a.entry - b.entry);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Globals whose meaning is fixed by SCI's system scripts. */
|
|
167
|
+
const wellKnownGlobals: Record<number, string> = { 0: "ego", 1: "theGame" };
|
|
168
|
+
|
|
169
|
+
/** What produced a value on the stack, as far as a single linear pass can tell. */
|
|
170
|
+
type StackEntry = ValueFact & { offset: number };
|
|
171
|
+
|
|
172
|
+
const COMPARISONS = new Set(["eq?", "ne?", "gt?", "ge?", "lt?", "le?", "ugt?", "uge?", "ult?", "ule?"]);
|
|
173
|
+
const valueOf = ({ offset: _, ...v }: StackEntry): ValueFact => v;
|
|
174
|
+
|
|
175
|
+
function annotate(fn: DisasmFunction, script: Script, ctx: DisasmContext, labels: Map<number, string>) {
|
|
176
|
+
const { comments } = fn;
|
|
177
|
+
const sel = (n: number) => ctx.selectorNames[n] ?? `sel_${n}`;
|
|
178
|
+
const props = fn.owner && ctx.propSelectors(fn.owner);
|
|
179
|
+
const heapLabel = (o: number) => {
|
|
180
|
+
const obj = script.objects.find((x) => x.offset === o);
|
|
181
|
+
if (obj) return obj.name;
|
|
182
|
+
const s = readString(script.heap, o);
|
|
183
|
+
return s ? JSON.stringify(s.length > 60 ? `${s.slice(0, 57)}...` : s) : undefined;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const { facts } = fn;
|
|
187
|
+
let stack: StackEntry[] = [];
|
|
188
|
+
let acc: ValueFact = {};
|
|
189
|
+
const push = (ins: Instruction, value?: number, v: ValueFact = {}) => stack.push({ ...v, ...(value === undefined ? {} : { const: value }), offset: ins.offset });
|
|
190
|
+
const pop = (n: number) => stack.splice(Math.max(0, stack.length - n), n);
|
|
191
|
+
/** Arguments of a call frame: [argc, ...args]. */
|
|
192
|
+
const callArgs = (frame: StackEntry[]) => frame.slice(1).map(valueOf);
|
|
193
|
+
|
|
194
|
+
/** Splits a send frame into `selector(args)` messages; skips it if the frame doesn't parse. */
|
|
195
|
+
const describeSend = (frame: StackEntry[], receiver: ValueFact, offset: number): string | undefined => {
|
|
196
|
+
const parts: string[] = [];
|
|
197
|
+
const sends: SendFact[] = [];
|
|
198
|
+
for (let i = 0; i < frame.length; ) {
|
|
199
|
+
const s = frame[i]?.const;
|
|
200
|
+
const argc = frame[i + 1]?.const;
|
|
201
|
+
if (s === undefined || argc === undefined || i + 2 + argc > frame.length) return undefined;
|
|
202
|
+
comments.set(frame[i]!.offset, sel(s));
|
|
203
|
+
parts.push(`${sel(s)}${argc ? `(${argc} arg${argc > 1 ? "s" : ""})` : ""}`);
|
|
204
|
+
sends.push({ offset, receiver, selector: s, args: frame.slice(i + 2, i + 2 + argc).map(valueOf) });
|
|
205
|
+
i += 2 + argc;
|
|
206
|
+
}
|
|
207
|
+
facts.sends.push(...sends);
|
|
208
|
+
return parts.join(", ");
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
for (const ins of fn.instructions) {
|
|
212
|
+
const [a = 0, b = 0] = ins.operands;
|
|
213
|
+
const o = ins.opcode;
|
|
214
|
+
|
|
215
|
+
if (labels.has(ins.offset) && ins.offset !== fn.entry) {
|
|
216
|
+
// A jump target: several paths meet here, so forget acc. Compiled code keeps the stack
|
|
217
|
+
// balanced across branches (switch statements keep their subject on it), so keep that.
|
|
218
|
+
acc = {};
|
|
219
|
+
}
|
|
220
|
+
const propSel = (offset: number) => props?.[offset / 2];
|
|
221
|
+
let nextAcc: ValueFact = {};
|
|
222
|
+
|
|
223
|
+
switch (ins.name) {
|
|
224
|
+
case "push": push(ins, undefined, acc); break;
|
|
225
|
+
case "dup": { const top = stack.at(-1); push(ins, undefined, top ? valueOf(top) : {}); break; }
|
|
226
|
+
case "pushSelf": push(ins, undefined, { self: true }); break;
|
|
227
|
+
case "pTos": { const s = propSel(a); push(ins, undefined, s === undefined ? {} : { prop: s }); break; }
|
|
228
|
+
case "ipTos": case "dpTos": push(ins); break;
|
|
229
|
+
case "pushi": push(ins, a); break;
|
|
230
|
+
case "push0": push(ins, 0); break;
|
|
231
|
+
case "push1": push(ins, 1); break;
|
|
232
|
+
case "push2": push(ins, 2); break;
|
|
233
|
+
case "lofss": push(ins, undefined, { heap: a }); break;
|
|
234
|
+
case "toss": pop(1); break;
|
|
235
|
+
case "&rest": stack = []; break; // pushes an unknown number of values
|
|
236
|
+
case "ldi": nextAcc = { const: a }; break;
|
|
237
|
+
case "lofsa": nextAcc = { heap: a }; break;
|
|
238
|
+
case "selfID": nextAcc = { self: true }; break;
|
|
239
|
+
case "pToa": { const s = propSel(a); if (s !== undefined) nextAcc = { prop: s }; break; }
|
|
240
|
+
case "call": pop(ins.operands.at(-1)! / 2 + 1); break;
|
|
241
|
+
case "callk": case "callb": case "calle": {
|
|
242
|
+
const frame = pop(ins.operands.at(-1)! / 2 + 1);
|
|
243
|
+
const call: CallFact = ins.name === "callk"
|
|
244
|
+
? { offset: ins.offset, kind: "kernel", target: a, args: callArgs(frame) }
|
|
245
|
+
: ins.name === "callb"
|
|
246
|
+
? { offset: ins.offset, kind: "export", target: 0, index: a, args: callArgs(frame) }
|
|
247
|
+
: { offset: ins.offset, kind: "export", target: a, index: b, args: callArgs(frame) };
|
|
248
|
+
facts.calls.push(call);
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
case "send": case "self": case "super": {
|
|
252
|
+
const frame = pop(ins.operands.at(-1)! / 2);
|
|
253
|
+
const receiver: ValueFact = ins.name === "send" ? acc : { self: true };
|
|
254
|
+
const msg = describeSend(frame, receiver, ins.offset);
|
|
255
|
+
const who = ins.name === "super" ? `super ${ctx.className(a) ?? a}` : ins.name === "self" ? "self" : "acc";
|
|
256
|
+
if (msg) comments.set(ins.offset, `${who}: ${msg}`);
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
default:
|
|
260
|
+
if ((o >= 0x01 && o <= 0x0a) || (o >= 0x0d && o <= 0x16)) {
|
|
261
|
+
// Binary ops: stack top <op> acc.
|
|
262
|
+
const [left] = pop(1);
|
|
263
|
+
if (COMPARISONS.has(ins.name) && left?.global !== undefined && acc.const !== undefined) {
|
|
264
|
+
facts.compares.push({ offset: ins.offset, global: left.global, op: ins.name, value: acc.const });
|
|
265
|
+
}
|
|
266
|
+
} else if (o === 0x17 || o === 0x18) { /* branches read acc */ nextAcc = acc; }
|
|
267
|
+
else if (o >= 0x40) {
|
|
268
|
+
const action = o >> 4; // 4 load, 5 store, 6 inc, 7 dec
|
|
269
|
+
const toStack = (o & 4) !== 0;
|
|
270
|
+
const indexed = (o & 8) !== 0;
|
|
271
|
+
const v: ValueFact = varKind(o) === "global" && !indexed ? { global: a } : {};
|
|
272
|
+
if (action === 5 && toStack) pop(1);
|
|
273
|
+
else if (action !== 5 && toStack) push(ins, undefined, action === 4 ? v : {});
|
|
274
|
+
else if (action === 4) nextAcc = v;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
acc = nextAcc;
|
|
278
|
+
|
|
279
|
+
// Operand names
|
|
280
|
+
switch (ins.name) {
|
|
281
|
+
case "callk": comments.set(ins.offset, `${ctx.kernelNames[a] ?? `kernel_${a}`}`); break;
|
|
282
|
+
case "call": comments.set(ins.offset, labels.get(ins.target!) ?? `proc_${hex(ins.target!)}`); break;
|
|
283
|
+
case "callb": comments.set(ins.offset, `script 0 export ${a}`); break;
|
|
284
|
+
case "calle": comments.set(ins.offset, `script ${a} export ${b}`); break;
|
|
285
|
+
case "class": comments.set(ins.offset, ctx.className(a) ?? `class ${a}`); break;
|
|
286
|
+
case "lofsa": case "lofss": { const l = heapLabel(a); if (l) comments.set(ins.offset, l); break; }
|
|
287
|
+
case "pToa": case "aTop": case "pTos": case "sTop": case "ipToa": case "dpToa": case "ipTos": case "dpTos": {
|
|
288
|
+
const s = props?.[a / 2];
|
|
289
|
+
if (s !== undefined) comments.set(ins.offset, sel(s));
|
|
290
|
+
break;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (o >= 0x40) {
|
|
294
|
+
const kind = varKind(o)!;
|
|
295
|
+
const name = kind === "global" && wellKnownGlobals[a] ? wellKnownGlobals[a] : `${kind}${a}`;
|
|
296
|
+
if (!comments.has(ins.offset)) comments.set(ins.offset, name);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** Plain-text listing, one function per block. */
|
|
302
|
+
export function formatDisassembly(script: Script, functions: DisasmFunction[]): string {
|
|
303
|
+
const out: string[] = [`; script ${script.number}`];
|
|
304
|
+
for (const obj of script.objects) {
|
|
305
|
+
out.push(`; ${obj.isClass ? "class" : "instance"} ${obj.name} @heap ${hex(obj.offset)} (species ${obj.isClass ? obj.species : "-"}, super ${obj.superclass})`);
|
|
306
|
+
}
|
|
307
|
+
for (const e of script.exports) out.push(`; export ${e.index}: ${e.kind} @${hex(e.offset)}`);
|
|
308
|
+
|
|
309
|
+
for (const fn of functions) {
|
|
310
|
+
const targets = new Set(fn.instructions.filter((i) => i.target !== undefined && i.name !== "call").map((i) => i.target!));
|
|
311
|
+
out.push("", `(${fn.label})`);
|
|
312
|
+
for (const ins of fn.instructions) {
|
|
313
|
+
if (targets.has(ins.offset)) out.push(` code_${hex(ins.offset)}:`);
|
|
314
|
+
const bytes = [...script.code.subarray(ins.offset, ins.offset + ins.length)].map((b) => hex(b, 2)).join(" ");
|
|
315
|
+
const args = ins.target !== undefined
|
|
316
|
+
? [ins.name === "call" ? `proc_${hex(ins.target)}` : `code_${hex(ins.target)}`, ...ins.operands.slice(1)]
|
|
317
|
+
: ins.operands.map((n) => (n > 9 ? `$${hex(n)}` : String(n)));
|
|
318
|
+
const comment = fn.comments.get(ins.offset);
|
|
319
|
+
out.push(` ${hex(ins.offset)}: ${bytes.padEnd(18)} ${ins.name.padEnd(8)} ${args.join(" ").padEnd(16)}${comment ? `; ${comment}` : ""}`.trimEnd());
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return out.join("\n");
|
|
323
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCI2 kernel function names, indexed by kernel number (the `callk` operand).
|
|
3
|
+
* As the SCI2 interpreter names them: a NUL-separated table in its executable, right
|
|
4
|
+
* after the "intrpt.cpp" debug string. "Dummy" entries are unused slots that keep numbering stable.
|
|
5
|
+
*/
|
|
6
|
+
export declare const kernelNames: readonly string[];
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCI2 kernel function names, indexed by kernel number (the `callk` operand).
|
|
3
|
+
* As the SCI2 interpreter names them: a NUL-separated table in its executable, right
|
|
4
|
+
* after the "intrpt.cpp" debug string. "Dummy" entries are unused slots that keep numbering stable.
|
|
5
|
+
*/
|
|
6
|
+
export const kernelNames = [
|
|
7
|
+
"Load",
|
|
8
|
+
"Unload",
|
|
9
|
+
"ScriptID",
|
|
10
|
+
"DisposeScript",
|
|
11
|
+
"Lock",
|
|
12
|
+
"ResCheck",
|
|
13
|
+
"Purge",
|
|
14
|
+
"Clone",
|
|
15
|
+
"DisposeClone",
|
|
16
|
+
"RespondsTo",
|
|
17
|
+
"SetNowSeen",
|
|
18
|
+
"NumLoops",
|
|
19
|
+
"NumCels",
|
|
20
|
+
"CelWide",
|
|
21
|
+
"CelHigh",
|
|
22
|
+
"GetHighPlanePri",
|
|
23
|
+
"GetHighItemPri",
|
|
24
|
+
"ShakeScreen",
|
|
25
|
+
"IsOnMe",
|
|
26
|
+
"ShowMovie",
|
|
27
|
+
"SetVideoMode",
|
|
28
|
+
"AddScreenItem",
|
|
29
|
+
"DeleteScreenItem",
|
|
30
|
+
"UpdateScreenItem",
|
|
31
|
+
"FrameOut",
|
|
32
|
+
"AddPlane",
|
|
33
|
+
"DeletePlane",
|
|
34
|
+
"UpdatePlane",
|
|
35
|
+
"RepaintPlane",
|
|
36
|
+
"SetShowStyle",
|
|
37
|
+
"ShowStylePercent",
|
|
38
|
+
"SetScroll",
|
|
39
|
+
"AddMagnify",
|
|
40
|
+
"DeleteMagnify",
|
|
41
|
+
"IsHiRes",
|
|
42
|
+
"Robot",
|
|
43
|
+
"InvertRect",
|
|
44
|
+
"TextSize",
|
|
45
|
+
"Message",
|
|
46
|
+
"TextColors",
|
|
47
|
+
"TextFonts",
|
|
48
|
+
"Dummy",
|
|
49
|
+
"SetQuitStr",
|
|
50
|
+
"EditText",
|
|
51
|
+
"InputText",
|
|
52
|
+
"CreateTextBitmap",
|
|
53
|
+
"Priority",
|
|
54
|
+
"GetEvent",
|
|
55
|
+
"GlobalToLocal",
|
|
56
|
+
"LocalToGlobal",
|
|
57
|
+
"MapKeyToDir",
|
|
58
|
+
"HaveMouse",
|
|
59
|
+
"SetCursor",
|
|
60
|
+
"VibrateMouse",
|
|
61
|
+
"SaveGame",
|
|
62
|
+
"RestoreGame",
|
|
63
|
+
"RestartGame",
|
|
64
|
+
"GameIsRestarting",
|
|
65
|
+
"MakeSaveCatName",
|
|
66
|
+
"MakeSaveFileName",
|
|
67
|
+
"GetSaveFiles",
|
|
68
|
+
"GetSaveDir",
|
|
69
|
+
"CheckSaveGame",
|
|
70
|
+
"CheckFreeSpace",
|
|
71
|
+
"DoSound",
|
|
72
|
+
"DoAudio",
|
|
73
|
+
"DoSync",
|
|
74
|
+
"NewList",
|
|
75
|
+
"DisposeList",
|
|
76
|
+
"NewNode",
|
|
77
|
+
"FirstNode",
|
|
78
|
+
"LastNode",
|
|
79
|
+
"EmptyList",
|
|
80
|
+
"NextNode",
|
|
81
|
+
"PrevNode",
|
|
82
|
+
"NodeValue",
|
|
83
|
+
"AddAfter",
|
|
84
|
+
"AddToFront",
|
|
85
|
+
"AddToEnd",
|
|
86
|
+
"Dummy",
|
|
87
|
+
"Dummy",
|
|
88
|
+
"FindKey",
|
|
89
|
+
"Dummy",
|
|
90
|
+
"Dummy",
|
|
91
|
+
"Dummy",
|
|
92
|
+
"DeleteKey",
|
|
93
|
+
"Dummy",
|
|
94
|
+
"Dummy",
|
|
95
|
+
"ListAt",
|
|
96
|
+
"ListIndexOf",
|
|
97
|
+
"ListEachElementDo",
|
|
98
|
+
"ListFirstTrue",
|
|
99
|
+
"ListAllTrue",
|
|
100
|
+
"Random",
|
|
101
|
+
"Abs",
|
|
102
|
+
"Sqrt",
|
|
103
|
+
"GetAngle",
|
|
104
|
+
"GetDistance",
|
|
105
|
+
"ATan",
|
|
106
|
+
"SinMult",
|
|
107
|
+
"CosMult",
|
|
108
|
+
"SinDiv",
|
|
109
|
+
"CosDiv",
|
|
110
|
+
"GetTime",
|
|
111
|
+
"Platform",
|
|
112
|
+
"BaseSetter",
|
|
113
|
+
"DirLoop",
|
|
114
|
+
"CantBeHere",
|
|
115
|
+
"InitBresen",
|
|
116
|
+
"DoBresen",
|
|
117
|
+
"SetJump",
|
|
118
|
+
"AvoidPath",
|
|
119
|
+
"InPolygon",
|
|
120
|
+
"MergePoly",
|
|
121
|
+
"Dummy",
|
|
122
|
+
"Dummy",
|
|
123
|
+
"MemoryInfo",
|
|
124
|
+
"Dummy",
|
|
125
|
+
"Dummy",
|
|
126
|
+
"Dummy",
|
|
127
|
+
"Dummy",
|
|
128
|
+
"Dummy",
|
|
129
|
+
"GetCWD",
|
|
130
|
+
"ValidPath",
|
|
131
|
+
"FileIO",
|
|
132
|
+
"Dummy",
|
|
133
|
+
"DeviceInfo",
|
|
134
|
+
"Palette",
|
|
135
|
+
"PalVary",
|
|
136
|
+
"PalCycle",
|
|
137
|
+
"Array",
|
|
138
|
+
"String",
|
|
139
|
+
"RemapColors",
|
|
140
|
+
"Dummy",
|
|
141
|
+
"Dummy",
|
|
142
|
+
"ObjectIntersect",
|
|
143
|
+
"Dummy",
|
|
144
|
+
"TextWidth",
|
|
145
|
+
"PointSize",
|
|
146
|
+
"AddLine",
|
|
147
|
+
"DeleteLine",
|
|
148
|
+
"UpdateLine",
|
|
149
|
+
"AddPolygon",
|
|
150
|
+
"DeletePolygon",
|
|
151
|
+
"UpdatePolygon",
|
|
152
|
+
"Bitmap",
|
|
153
|
+
"ScrollWindow",
|
|
154
|
+
"SetFontRes",
|
|
155
|
+
"MovePlaneItems",
|
|
156
|
+
"PreloadResource",
|
|
157
|
+
"Dummy",
|
|
158
|
+
"ResourceTrack",
|
|
159
|
+
"CheckCDisc",
|
|
160
|
+
"GetSaveCDisc",
|
|
161
|
+
"Dummy",
|
|
162
|
+
"Dummy",
|
|
163
|
+
"LoadChunk",
|
|
164
|
+
"SetPalStyleRange",
|
|
165
|
+
];
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCI2 kernel function names, indexed by kernel number (the `callk` operand).
|
|
3
|
+
* As the SCI2 interpreter names them: a NUL-separated table in its executable, right
|
|
4
|
+
* after the "intrpt.cpp" debug string. "Dummy" entries are unused slots that keep numbering stable.
|
|
5
|
+
*/
|
|
6
|
+
export const kernelNames: readonly string[] = [
|
|
7
|
+
"Load",
|
|
8
|
+
"Unload",
|
|
9
|
+
"ScriptID",
|
|
10
|
+
"DisposeScript",
|
|
11
|
+
"Lock",
|
|
12
|
+
"ResCheck",
|
|
13
|
+
"Purge",
|
|
14
|
+
"Clone",
|
|
15
|
+
"DisposeClone",
|
|
16
|
+
"RespondsTo",
|
|
17
|
+
"SetNowSeen",
|
|
18
|
+
"NumLoops",
|
|
19
|
+
"NumCels",
|
|
20
|
+
"CelWide",
|
|
21
|
+
"CelHigh",
|
|
22
|
+
"GetHighPlanePri",
|
|
23
|
+
"GetHighItemPri",
|
|
24
|
+
"ShakeScreen",
|
|
25
|
+
"IsOnMe",
|
|
26
|
+
"ShowMovie",
|
|
27
|
+
"SetVideoMode",
|
|
28
|
+
"AddScreenItem",
|
|
29
|
+
"DeleteScreenItem",
|
|
30
|
+
"UpdateScreenItem",
|
|
31
|
+
"FrameOut",
|
|
32
|
+
"AddPlane",
|
|
33
|
+
"DeletePlane",
|
|
34
|
+
"UpdatePlane",
|
|
35
|
+
"RepaintPlane",
|
|
36
|
+
"SetShowStyle",
|
|
37
|
+
"ShowStylePercent",
|
|
38
|
+
"SetScroll",
|
|
39
|
+
"AddMagnify",
|
|
40
|
+
"DeleteMagnify",
|
|
41
|
+
"IsHiRes",
|
|
42
|
+
"Robot",
|
|
43
|
+
"InvertRect",
|
|
44
|
+
"TextSize",
|
|
45
|
+
"Message",
|
|
46
|
+
"TextColors",
|
|
47
|
+
"TextFonts",
|
|
48
|
+
"Dummy",
|
|
49
|
+
"SetQuitStr",
|
|
50
|
+
"EditText",
|
|
51
|
+
"InputText",
|
|
52
|
+
"CreateTextBitmap",
|
|
53
|
+
"Priority",
|
|
54
|
+
"GetEvent",
|
|
55
|
+
"GlobalToLocal",
|
|
56
|
+
"LocalToGlobal",
|
|
57
|
+
"MapKeyToDir",
|
|
58
|
+
"HaveMouse",
|
|
59
|
+
"SetCursor",
|
|
60
|
+
"VibrateMouse",
|
|
61
|
+
"SaveGame",
|
|
62
|
+
"RestoreGame",
|
|
63
|
+
"RestartGame",
|
|
64
|
+
"GameIsRestarting",
|
|
65
|
+
"MakeSaveCatName",
|
|
66
|
+
"MakeSaveFileName",
|
|
67
|
+
"GetSaveFiles",
|
|
68
|
+
"GetSaveDir",
|
|
69
|
+
"CheckSaveGame",
|
|
70
|
+
"CheckFreeSpace",
|
|
71
|
+
"DoSound",
|
|
72
|
+
"DoAudio",
|
|
73
|
+
"DoSync",
|
|
74
|
+
"NewList",
|
|
75
|
+
"DisposeList",
|
|
76
|
+
"NewNode",
|
|
77
|
+
"FirstNode",
|
|
78
|
+
"LastNode",
|
|
79
|
+
"EmptyList",
|
|
80
|
+
"NextNode",
|
|
81
|
+
"PrevNode",
|
|
82
|
+
"NodeValue",
|
|
83
|
+
"AddAfter",
|
|
84
|
+
"AddToFront",
|
|
85
|
+
"AddToEnd",
|
|
86
|
+
"Dummy",
|
|
87
|
+
"Dummy",
|
|
88
|
+
"FindKey",
|
|
89
|
+
"Dummy",
|
|
90
|
+
"Dummy",
|
|
91
|
+
"Dummy",
|
|
92
|
+
"DeleteKey",
|
|
93
|
+
"Dummy",
|
|
94
|
+
"Dummy",
|
|
95
|
+
"ListAt",
|
|
96
|
+
"ListIndexOf",
|
|
97
|
+
"ListEachElementDo",
|
|
98
|
+
"ListFirstTrue",
|
|
99
|
+
"ListAllTrue",
|
|
100
|
+
"Random",
|
|
101
|
+
"Abs",
|
|
102
|
+
"Sqrt",
|
|
103
|
+
"GetAngle",
|
|
104
|
+
"GetDistance",
|
|
105
|
+
"ATan",
|
|
106
|
+
"SinMult",
|
|
107
|
+
"CosMult",
|
|
108
|
+
"SinDiv",
|
|
109
|
+
"CosDiv",
|
|
110
|
+
"GetTime",
|
|
111
|
+
"Platform",
|
|
112
|
+
"BaseSetter",
|
|
113
|
+
"DirLoop",
|
|
114
|
+
"CantBeHere",
|
|
115
|
+
"InitBresen",
|
|
116
|
+
"DoBresen",
|
|
117
|
+
"SetJump",
|
|
118
|
+
"AvoidPath",
|
|
119
|
+
"InPolygon",
|
|
120
|
+
"MergePoly",
|
|
121
|
+
"Dummy",
|
|
122
|
+
"Dummy",
|
|
123
|
+
"MemoryInfo",
|
|
124
|
+
"Dummy",
|
|
125
|
+
"Dummy",
|
|
126
|
+
"Dummy",
|
|
127
|
+
"Dummy",
|
|
128
|
+
"Dummy",
|
|
129
|
+
"GetCWD",
|
|
130
|
+
"ValidPath",
|
|
131
|
+
"FileIO",
|
|
132
|
+
"Dummy",
|
|
133
|
+
"DeviceInfo",
|
|
134
|
+
"Palette",
|
|
135
|
+
"PalVary",
|
|
136
|
+
"PalCycle",
|
|
137
|
+
"Array",
|
|
138
|
+
"String",
|
|
139
|
+
"RemapColors",
|
|
140
|
+
"Dummy",
|
|
141
|
+
"Dummy",
|
|
142
|
+
"ObjectIntersect",
|
|
143
|
+
"Dummy",
|
|
144
|
+
"TextWidth",
|
|
145
|
+
"PointSize",
|
|
146
|
+
"AddLine",
|
|
147
|
+
"DeleteLine",
|
|
148
|
+
"UpdateLine",
|
|
149
|
+
"AddPolygon",
|
|
150
|
+
"DeletePolygon",
|
|
151
|
+
"UpdatePolygon",
|
|
152
|
+
"Bitmap",
|
|
153
|
+
"ScrollWindow",
|
|
154
|
+
"SetFontRes",
|
|
155
|
+
"MovePlaneItems",
|
|
156
|
+
"PreloadResource",
|
|
157
|
+
"Dummy",
|
|
158
|
+
"ResourceTrack",
|
|
159
|
+
"CheckCDisc",
|
|
160
|
+
"GetSaveCDisc",
|
|
161
|
+
"Dummy",
|
|
162
|
+
"Dummy",
|
|
163
|
+
"LoadChunk",
|
|
164
|
+
"SetPalStyleRange",
|
|
165
|
+
];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SCI bytecode. Each instruction's first byte is `opcode << 1 | size`: when the low bit is
|
|
3
|
+
* set, "sized" operands are 1 byte instead of 2.
|
|
4
|
+
*
|
|
5
|
+
* Operand kinds:
|
|
6
|
+
* W sized unsigned (word or byte) S sized signed
|
|
7
|
+
* R sized signed, relative to the next instruction (jump/call target)
|
|
8
|
+
* B always 1 byte U always 2 bytes
|
|
9
|
+
*
|
|
10
|
+
* SCI2 quirk, found by measuring: the argument-frame operand of call/callk/callb/calle/super
|
|
11
|
+
* is always 16-bit, even in the byte form; send and self frames follow the size bit.
|
|
12
|
+
* (SCI0–1.1 used a fixed byte.) Decoding it as a byte leaves a stray 0x00, which shows up
|
|
13
|
+
* as thousands of bogus `bnot` instructions.
|
|
14
|
+
*/
|
|
15
|
+
export type OperandKind = "W" | "S" | "R" | "B" | "U";
|
|
16
|
+
export interface OpcodeInfo {
|
|
17
|
+
name: string;
|
|
18
|
+
operands: OperandKind[];
|
|
19
|
+
}
|
|
20
|
+
export declare const opcodes: readonly OpcodeInfo[];
|
|
21
|
+
export type VarKind = "global" | "local" | "temp" | "param";
|
|
22
|
+
export declare const varKind: (opcode: number) => VarKind | undefined;
|