@henols/c64-re-tools 2.0.0-rc.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -115
- package/package.json +1 -1
- package/skills/c64-assembler/SKILL.md +22 -6
- package/skills/c64-assembler/scripts/assemble.ts +42 -14
- package/skills/c64-assembler/scripts/lib/compare.ts +48 -0
- package/skills/c64-basic/SKILL.md +10 -1
- package/skills/c64-basic/scripts/basic.ts +3 -3
- package/skills/c64-disk/SKILL.md +13 -4
- package/skills/c64-disk/scripts/disk.ts +7 -7
- package/skills/c64-disk/scripts/{containers.ts → lib/containers.ts} +2 -2
- package/skills/c64-docs/SKILL.md +138 -0
- package/skills/c64-docs/references/page-types.md +141 -0
- package/skills/c64-docs/references/style.md +43 -0
- package/skills/c64-docs/scripts/generate.ts +109 -0
- package/skills/c64-docs/scripts/lib/links.ts +167 -0
- package/skills/c64-docs/scripts/lib/markdown.ts +79 -0
- package/skills/c64-docs/scripts/lib/reference.ts +406 -0
- package/skills/c64-docs/scripts/lib/source-map.ts +81 -0
- package/skills/c64-docs/scripts/link.ts +239 -0
- package/skills/c64-emulator/SKILL.md +16 -5
- package/skills/c64-knowledge/SKILL.md +140 -18
- package/skills/c64-knowledge/scripts/knowledge.ts +737 -112
- package/skills/c64-knowledge/scripts/lib/listing.ts +162 -0
- package/skills/c64-memory-map/SKILL.md +11 -2
- package/skills/c64-memory-map/scripts/memmap.ts +2 -2
- package/skills/c64-provenance/SKILL.md +66 -11
- package/skills/c64-provenance/scripts/{compare.ts → lib/compare.ts} +3 -3
- package/skills/c64-provenance/scripts/lib/library.ts +584 -0
- package/skills/c64-provenance/scripts/{runtime.ts → lib/runtime.ts} +2 -2
- package/skills/c64-provenance/scripts/{separate.ts → lib/separate.ts} +3 -3
- package/skills/c64-provenance/scripts/provenance.ts +242 -40
- package/skills/c64-reverse-engineering/SKILL.md +51 -26
- package/skills/c64-source/SKILL.md +76 -28
- package/skills/c64-source/scripts/generate.ts +425 -127
- package/skills/c64-source/scripts/{loader.ts → lib/loader.ts} +84 -25
- package/skills/c64-source/scripts/lib/source.ts +1121 -0
- package/skills/c64-static-analysis/SKILL.md +35 -18
- package/skills/c64-static-analysis/scripts/analyze.ts +184 -55
- package/skills/c64-static-analysis/scripts/{coverage.ts → lib/coverage.ts} +19 -25
- package/skills/c64-static-analysis/scripts/{findings.ts → lib/findings.ts} +6 -2
- package/skills/c64-static-analysis/scripts/{idioms.ts → lib/idioms.ts} +21 -12
- package/skills/c64-static-analysis/scripts/{instructions.ts → lib/instructions.ts} +1 -1
- package/skills/c64-static-analysis/scripts/lib/macros.ts +233 -0
- package/skills/c64-static-analysis/scripts/lib/pitfalls.ts +68 -0
- package/skills/c64-static-analysis/scripts/lib/pointers.ts +108 -0
- package/skills/c64-testing/SKILL.md +36 -7
- package/skills/c64-testing/references/scenario-format.md +12 -8
- package/skills/c64-testing/scripts/checklist.ts +3 -3
- package/skills/c64-testing/scripts/diff.ts +167 -0
- package/skills/c64-testing/scripts/lib/diff.ts +70 -0
- package/skills/c64-testing/scripts/{scenario.ts → lib/scenario.ts} +45 -34
- package/skills/c64-testing/scripts/run.ts +73 -21
- package/skills/c64-testing/scripts/test.ts +5 -5
- package/skills/c64-unpacker/SKILL.md +23 -9
- package/skills/c64-unpacker/scripts/{evidence.ts → lib/evidence.ts} +1 -1
- package/skills/c64-unpacker/scripts/{loads.ts → lib/loads.ts} +4 -4
- package/skills/c64-unpacker/scripts/{machine.ts → lib/machine.ts} +4 -4
- package/skills/c64-unpacker/scripts/{stages.ts → lib/stages.ts} +17 -5
- package/skills/c64-unpacker/scripts/unpack.ts +41 -24
- package/src/c64.ts +103 -5
- package/src/cli/cleanup.ts +1 -1
- package/src/cli/main.ts +7 -4
- package/src/cli/status.ts +70 -3
- package/src/graphics.ts +158 -0
- package/src/host/main.ts +43 -8
- package/src/host/server.ts +10 -3
- package/src/host/tools/petcat.ts +1 -1
- package/src/host/tools/status.ts +1 -1
- package/src/host/vice/adapter.ts +4 -3
- package/src/host/vice/process.ts +2 -2
- package/src/host/vice/screen.ts +1 -42
- package/src/host/vice/session.ts +196 -23
- package/src/host-client/autostart.ts +1 -1
- package/src/host-client/connect.ts +1 -1
- package/src/host-client/coverage.ts +2 -29
- package/src/host-client/image.ts +66 -0
- package/src/host-client/steps.ts +9 -4
- package/src/host-client/tools.ts +1 -0
- package/src/host-client/transfer.ts +39 -11
- package/src/host-client/vice-session.ts +33 -16
- package/src/knowledge/conflicts.ts +161 -0
- package/src/knowledge/database.ts +150 -38
- package/src/knowledge/expression.ts +209 -0
- package/src/knowledge/graph.ts +104 -0
- package/src/knowledge/history.ts +70 -22
- package/src/knowledge/import.ts +64 -55
- package/src/knowledge/join.ts +211 -0
- package/src/knowledge/program.ts +266 -0
- package/src/knowledge/read.ts +258 -27
- package/src/knowledge/schema.ts +199 -75
- package/src/knowledge/text.ts +415 -0
- package/src/knowledge/write.ts +740 -57
- package/src/mcp/main.ts +1 -1
- package/src/mcp/output.ts +15 -8
- package/src/mcp/server.ts +3 -1
- package/src/mcp/tools/debug.ts +97 -16
- package/src/mcp/tools/execution.ts +38 -5
- package/src/mcp/tools/machine.ts +11 -2
- package/src/mcp/tools/media.ts +18 -13
- package/src/mcp/tools/memory.ts +17 -8
- package/src/mcp/tools/video.ts +9 -9
- package/src/native/discover.ts +2 -2
- package/src/native/ghidra/analyze.ts +2 -2
- package/src/native/ghidra/index.ts +1 -1
- package/src/native/ghidra/language/c64rt_6510.slaspec +4 -1
- package/src/native/ghidra/scripts/C64Export.java +1 -1
- package/src/native/ghidra/scripts/C64Prepare.java +7 -1
- package/src/native/types.ts +14 -8
- package/{skills/c64-source/scripts → src}/opcodes.ts +75 -3
- package/src/platform.ts +70 -7
- package/src/project.ts +180 -3
- package/src/protocol/messages.ts +1 -1
- package/src/protocol/tools.ts +2 -16
- package/src/protocol/vice.ts +124 -15
- package/src/script.ts +46 -7
- package/src/trace.ts +55 -23
- package/skills/c64-assembler/scripts/compare.ts +0 -84
- package/skills/c64-source/scripts/source.ts +0 -595
package/src/host/main.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { ProcessSupervisor } from "../native/processes.ts";
|
|
|
7
7
|
import { startTrace, trace } from "../trace.ts";
|
|
8
8
|
import { ListenError, parsePort, startHostServer, stopSignal } from "./server.ts";
|
|
9
9
|
import { viceFlaws, type ViceVersion } from "../native/discover.ts";
|
|
10
|
+
import { inspect } from "./tools/c1541.ts";
|
|
10
11
|
import { createToolDispatcher } from "./tools/index.ts";
|
|
11
12
|
import { checkViceStarts, findVice } from "./vice/process.ts";
|
|
12
13
|
import { viceSessionFactory } from "./vice/session.ts";
|
|
@@ -15,7 +16,7 @@ import { PACKAGE } from "../package.ts";
|
|
|
15
16
|
const HELP = `c64-re-tools-host
|
|
16
17
|
|
|
17
18
|
Usage:
|
|
18
|
-
npx -y --package=${PACKAGE.name}@${PACKAGE.tag} c64-re-tools-host [--port <port>]
|
|
19
|
+
npx -y --package=${PACKAGE.name}@${PACKAGE.tag} c64-re-tools-host [--port <port>] [--log-console] [--log-dir <directory>]
|
|
19
20
|
c64-re-tools-host --help
|
|
20
21
|
|
|
21
22
|
Runs the c64-re-tools Host Runtime in the foreground. It first starts VICE
|
|
@@ -27,8 +28,16 @@ connected c64-re-tools-mcp process, and runs the tools that come with VICE
|
|
|
27
28
|
emulator and tool it started.
|
|
28
29
|
|
|
29
30
|
Options:
|
|
30
|
-
--port <port>
|
|
31
|
-
--
|
|
31
|
+
--port <port> Listen on this port instead (0 picks a free port).
|
|
32
|
+
--log-console Also print each request with its result, and each run
|
|
33
|
+
of c1541 or petcat, to the console.
|
|
34
|
+
--log-dir <directory> Write the same events as JSON lines to a file in this
|
|
35
|
+
directory, with a copy of each VICE log.
|
|
36
|
+
--help Show this help.
|
|
37
|
+
|
|
38
|
+
The host always logs its start and stop, connections, failures, and each call
|
|
39
|
+
from a skill script (c1541, petcat) with its outcome. --log-console adds the
|
|
40
|
+
calls of the MCP server and the tool runs. C64RT_TRACE does not apply to the host.`;
|
|
32
41
|
|
|
33
42
|
function log(line: string): void {
|
|
34
43
|
process.stderr.write(`c64-re-tools-host: ${line}\n`);
|
|
@@ -36,9 +45,12 @@ function log(line: string): void {
|
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
async function main(): Promise<number> {
|
|
39
|
-
let values: { help?: boolean; port?: string };
|
|
48
|
+
let values: { help?: boolean; port?: string; "log-console"?: boolean; "log-dir"?: string };
|
|
40
49
|
try {
|
|
41
|
-
({ values } = parseArgs({
|
|
50
|
+
({ values } = parseArgs({
|
|
51
|
+
options: { help: { type: "boolean" }, port: { type: "string" }, "log-console": { type: "boolean" }, "log-dir": { type: "string" } },
|
|
52
|
+
strict: true,
|
|
53
|
+
}));
|
|
42
54
|
} catch (error) {
|
|
43
55
|
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n\n${HELP}\n`);
|
|
44
56
|
return 2;
|
|
@@ -47,7 +59,18 @@ async function main(): Promise<number> {
|
|
|
47
59
|
process.stdout.write(`${HELP}\n`);
|
|
48
60
|
return 0;
|
|
49
61
|
}
|
|
50
|
-
|
|
62
|
+
const logDir = values["log-dir"];
|
|
63
|
+
if (logDir === "") {
|
|
64
|
+
process.stderr.write("--log-dir needs a directory.\n");
|
|
65
|
+
return 2;
|
|
66
|
+
}
|
|
67
|
+
startTrace("host", {
|
|
68
|
+
target: {
|
|
69
|
+
...(logDir === undefined ? {} : { dir: { path: logDir, given: `--log-dir ${logDir}` } }),
|
|
70
|
+
...(values["log-console"] ? { echo: (line: string) => process.stderr.write(`c64-re-tools-host: ${line}\n`) } : {}),
|
|
71
|
+
},
|
|
72
|
+
warn: log,
|
|
73
|
+
});
|
|
51
74
|
|
|
52
75
|
const port = values.port === undefined ? DEFAULT_HOST_PORT : parsePort(values.port);
|
|
53
76
|
if (port === undefined) {
|
|
@@ -55,6 +78,9 @@ async function main(): Promise<number> {
|
|
|
55
78
|
return 2;
|
|
56
79
|
}
|
|
57
80
|
|
|
81
|
+
// First, so that every start, also a refused one, tells which runtime runs.
|
|
82
|
+
log(`${PACKAGE.name} ${PACKAGE.version} (Node.js ${process.version}, ${process.platform} ${process.arch})`);
|
|
83
|
+
|
|
58
84
|
// Without an emulator the runtime is useless: refuse to start, by name and with the remedy.
|
|
59
85
|
try {
|
|
60
86
|
findVice(process.env);
|
|
@@ -80,7 +106,7 @@ async function main(): Promise<number> {
|
|
|
80
106
|
}
|
|
81
107
|
log(`VICE ${vice.major}.${vice.minor}${vice.revision === undefined ? "" : ` r${vice.revision}`} starts`);
|
|
82
108
|
const flaws = viceFlaws(vice);
|
|
83
|
-
// A known VICE flaw is named, not worked around
|
|
109
|
+
// A known VICE flaw is named, not worked around.
|
|
84
110
|
for (const flaw of flaws) log(`Known VICE flaw: ${flaw}`);
|
|
85
111
|
|
|
86
112
|
let server;
|
|
@@ -89,7 +115,16 @@ async function main(): Promise<number> {
|
|
|
89
115
|
host: "0.0.0.0",
|
|
90
116
|
port,
|
|
91
117
|
log,
|
|
92
|
-
createViceSession: viceSessionFactory({
|
|
118
|
+
createViceSession: viceSessionFactory({
|
|
119
|
+
supervisor,
|
|
120
|
+
log,
|
|
121
|
+
flaws,
|
|
122
|
+
// c1541 lists the disk, so an autostart index selects a program.
|
|
123
|
+
directory: async (imageType, image) => {
|
|
124
|
+
const { result } = await inspect({ action: "directory", imageType }, image, { supervisor, signal: new AbortController().signal, log });
|
|
125
|
+
return result.action === "directory" ? result.entries : [];
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
93
128
|
tools: createToolDispatcher({ supervisor, log, vice }),
|
|
94
129
|
});
|
|
95
130
|
} catch (error) {
|
package/src/host/server.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { encodeFrame, MessageReader } from "../protocol/framing.ts";
|
|
|
4
4
|
import { checkHello, isOneOf, HEARTBEAT_TIMEOUT_MS, parseClientMessage, ProtocolError, type HostMessage, type ReceivedRequest, type VideoStandard, type WireError, WireFailure } from "../protocol/messages.ts";
|
|
5
5
|
import { TOOL_OPERATIONS, validateToolParams, type ToolOperation, type ToolOperations } from "../protocol/tools.ts";
|
|
6
6
|
import { attachmentCount, VICE_OPERATIONS, validateViceParams, type ViceOperation, type ViceOperations } from "../protocol/vice.ts";
|
|
7
|
-
import { elapsedMs, trace } from "../trace.ts";
|
|
7
|
+
import { elapsedMs, summarize, trace } from "../trace.ts";
|
|
8
8
|
|
|
9
9
|
/** One live VICE session, owned by one connection. */
|
|
10
10
|
export interface ViceSessionHandle {
|
|
@@ -234,8 +234,15 @@ class Connection {
|
|
|
234
234
|
|
|
235
235
|
async #answer(request: ReceivedRequest, attachments: Buffer[]): Promise<void> {
|
|
236
236
|
const started = performance.now();
|
|
237
|
-
const record = (outcome: Record<string, unknown>) =>
|
|
238
|
-
|
|
237
|
+
const record = (outcome: Record<string, unknown>) => {
|
|
238
|
+
const ms = elapsedMs(started);
|
|
239
|
+
trace().event("request", { connection: this.#id, op: request.op, id: request.id, params: request.params, attachments: attachments.length, ms, ...outcome });
|
|
240
|
+
// A call from a skill script (c1541, petcat, status) is always logged; the many calls of a VICE session only in the trace.
|
|
241
|
+
if (this.#isTool) {
|
|
242
|
+
const what = outcome.ok === true ? "ok" : `${String(outcome.code)}: ${String(outcome.message)}`;
|
|
243
|
+
this.#log(`${request.op} ${JSON.stringify(summarize(request.params) ?? {})}: ${what} (${ms} ms)`);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
239
246
|
try {
|
|
240
247
|
let result: unknown;
|
|
241
248
|
let replyAttachments: Buffer[] = [];
|
package/src/host/tools/petcat.ts
CHANGED
|
@@ -18,7 +18,7 @@ const TIMEOUT_MS = 30_000;
|
|
|
18
18
|
const LISTING = "listing.txt";
|
|
19
19
|
/**
|
|
20
20
|
* petcat's listing lines (" 10 sys2061"), as number and text. petcat
|
|
21
|
-
* prints π ($ff) as `~`, which is no PETSCII character: the text has
|
|
21
|
+
* prints π ($ff) as `~`, which is no PETSCII character: the text has π.
|
|
22
22
|
*/
|
|
23
23
|
export function parseListing(listing: string): Array<{ number: number; text: string }> {
|
|
24
24
|
const lines: Array<{ number: number; text: string }> = [];
|
package/src/host/tools/status.ts
CHANGED
|
@@ -38,7 +38,7 @@ export async function hostStatus(context: ToolContext, started?: ViceVersion): P
|
|
|
38
38
|
);
|
|
39
39
|
const tools = started === undefined ? probed : [{ name: VICE.name, found: true, path: findVice(context.env ?? process.env), version: versionText(started), runs: true }, ...probed];
|
|
40
40
|
const checked = tools.map((status, index) => requireMinimum(status, specs[index]!));
|
|
41
|
-
// VICE runs with its known flaws; status names them
|
|
41
|
+
// VICE runs with its known flaws; status names them.
|
|
42
42
|
const vice = checked[0]!;
|
|
43
43
|
const version = vice.runs ? viceVersionOf(vice.version ?? "") : undefined;
|
|
44
44
|
const flaws = version === undefined ? [] : viceFlaws(version);
|
package/src/host/vice/adapter.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
import { WireFailure, type VideoStandard } from "../../protocol/messages.ts";
|
|
5
5
|
import { type Comparison, type Condition, type BacktraceFrame, type HistoryEntry, type Instruction, type MemmapRange, type ProfileEntry, type JoystickState, type MemoryView, type RegisterValues, type Registers, type Space } from "../../protocol/vice.ts";
|
|
6
6
|
import { COVERAGE, formatC64Address, formatC64Byte } from "../../c64.ts";
|
|
7
|
-
import {
|
|
7
|
+
import type { IndexedFrame } from "../../graphics.ts";
|
|
8
|
+
import { decodeDisplay, decodePalette, visibleFrame } from "./screen.ts";
|
|
8
9
|
import type { TextMonitor } from "./text-monitor.ts";
|
|
9
10
|
import type { CiaHidden } from "./chips.ts";
|
|
10
11
|
import {
|
|
@@ -260,7 +261,7 @@ function unknownForm(what: string, lines: string[]): WireFailure {
|
|
|
260
261
|
return new WireFailure("operation-failed", `VICE printed the ${what} in a form that c64-re-tools does not read: ${JSON.stringify(lines.slice(0, 3))}`);
|
|
261
262
|
}
|
|
262
263
|
|
|
263
|
-
/** The coverage byte
|
|
264
|
+
/** The coverage byte of one memmap row's I/O, ROM and RAM columns, each "rwx" with "-" for no access. */
|
|
264
265
|
export function memmapFlags(io: string, rom: string, ram: string): number {
|
|
265
266
|
let flags = 0;
|
|
266
267
|
if (ram[0] === "r") flags |= COVERAGE.ramRead;
|
|
@@ -434,7 +435,7 @@ export class ViceAdapter {
|
|
|
434
435
|
|
|
435
436
|
/**
|
|
436
437
|
* The timer latches and the interrupt mask of the CIA at `base`, from VICE's
|
|
437
|
-
* text-monitor dump: no register read gives them
|
|
438
|
+
* text-monitor dump: no register read gives them.
|
|
438
439
|
*/
|
|
439
440
|
async ciaHidden(base: 0xdc00 | 0xdd00): Promise<CiaHidden> {
|
|
440
441
|
const answer = await this.#computerText(`io ${hex4(base)}`);
|
package/src/host/vice/process.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// - autostart of a PRG needs `-autostartprgmode 1` (inject into RAM);
|
|
13
13
|
// - the joyport command reaches the C64 only through the "Joyport I/O
|
|
14
14
|
// simulation" device (37). In VICE 3.10 and older it holds bits 5-7 of
|
|
15
|
-
// both CIA 1 ports low (VICE bug #2202, a known flaw
|
|
15
|
+
// both CIA 1 ports low (VICE bug #2202, a known flaw);
|
|
16
16
|
// - with the default JAM action a jammed CPU hangs silently; -jamaction 2
|
|
17
17
|
// enters the monitor instead;
|
|
18
18
|
// - the monitor's profiler prints numbers in the host locale, so VICE runs
|
|
@@ -97,7 +97,7 @@ export function viceArguments(options: {
|
|
|
97
97
|
// Autostart injects a PRG into RAM; the default mode copies it to a disk image first.
|
|
98
98
|
"-autostartprgmode",
|
|
99
99
|
"1",
|
|
100
|
-
// VICE's default waits a random time before an autostart types its LOAD, so two runs of one file differ
|
|
100
|
+
// VICE's default waits a random time before an autostart types its LOAD, so two runs of one file differ.
|
|
101
101
|
"+autostart-delay-random",
|
|
102
102
|
// The same RAM after every power-up: a hard reset is a power cycle, and VICE's default can flip
|
|
103
103
|
// random bits, so two runs from one start state could differ.
|
package/src/host/vice/screen.ts
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
// Canonical emulator-frame capture: VICE's own rendered VIC-II frame, never a
|
|
2
2
|
// desktop screenshot; the frame comparison and difference image for baselines.
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import type { IndexedFrame } from "../../graphics.ts";
|
|
6
5
|
import type { VideoStandard } from "../../protocol/messages.ts";
|
|
7
6
|
import { FRAME_SIZE } from "../../protocol/vice.ts";
|
|
8
7
|
|
|
9
|
-
export interface IndexedFrame {
|
|
10
|
-
width: number;
|
|
11
|
-
height: number;
|
|
12
|
-
/** One palette index per pixel, row by row. */
|
|
13
|
-
pixels: Uint8Array;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
8
|
export interface RawDisplay {
|
|
17
9
|
/** Full buffer size, blanking included. */
|
|
18
10
|
bufferWidth: number;
|
|
@@ -83,39 +75,6 @@ export function decodePalette(body: Buffer): Array<[number, number, number]> {
|
|
|
83
75
|
return colours;
|
|
84
76
|
}
|
|
85
77
|
|
|
86
|
-
function chunk(type: string, data: Uint8Array): Buffer {
|
|
87
|
-
const length = Buffer.alloc(4);
|
|
88
|
-
length.writeUInt32BE(data.length, 0);
|
|
89
|
-
const body = Buffer.concat([Buffer.from(type, "latin1"), data]);
|
|
90
|
-
const crc = Buffer.alloc(4);
|
|
91
|
-
crc.writeUInt32BE(crc32(body), 0);
|
|
92
|
-
return Buffer.concat([length, body, crc]);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** Encodes an indexed frame as an 8-bit palette PNG. */
|
|
96
|
-
export function encodePng(frame: IndexedFrame, palette: ReadonlyArray<readonly [number, number, number]>): Buffer {
|
|
97
|
-
for (const index of frame.pixels) {
|
|
98
|
-
if (index >= palette.length) throw new Error(`pixel colour ${index} is not in the ${palette.length}-colour palette`);
|
|
99
|
-
}
|
|
100
|
-
const header = Buffer.alloc(13);
|
|
101
|
-
header.writeUInt32BE(frame.width, 0);
|
|
102
|
-
header.writeUInt32BE(frame.height, 4);
|
|
103
|
-
header[8] = 8; // bit depth
|
|
104
|
-
header[9] = 3; // indexed colour
|
|
105
|
-
const rows = Buffer.alloc((frame.width + 1) * frame.height);
|
|
106
|
-
for (let row = 0; row < frame.height; row++) {
|
|
107
|
-
// Filter byte 0 (none), then the row.
|
|
108
|
-
rows.set(frame.pixels.subarray(row * frame.width, (row + 1) * frame.width), row * (frame.width + 1) + 1);
|
|
109
|
-
}
|
|
110
|
-
return Buffer.concat([
|
|
111
|
-
Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
|
|
112
|
-
chunk("IHDR", header),
|
|
113
|
-
chunk("PLTE", Buffer.from(palette.flat())),
|
|
114
|
-
chunk("IDAT", deflateSync(rows)),
|
|
115
|
-
chunk("IEND", new Uint8Array(0)),
|
|
116
|
-
]);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
78
|
export interface FrameDifference {
|
|
120
79
|
mismatchingPixels: number;
|
|
121
80
|
/** Pixels compared: the frame minus the masked ones. */
|