picopilot 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +4 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +59 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/audio.d.ts +17 -0
- package/dist/commands/audio.d.ts.map +1 -0
- package/dist/commands/audio.js +315 -0
- package/dist/commands/audio.js.map +1 -0
- package/dist/commands/export.d.ts +36 -0
- package/dist/commands/export.d.ts.map +1 -0
- package/dist/commands/export.js +257 -0
- package/dist/commands/export.js.map +1 -0
- package/dist/commands/gfx.d.ts +28 -0
- package/dist/commands/gfx.d.ts.map +1 -0
- package/dist/commands/gfx.js +521 -0
- package/dist/commands/gfx.js.map +1 -0
- package/dist/commands/init.d.ts +34 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +151 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/lint.d.ts +20 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +90 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/minify.d.ts +20 -0
- package/dist/commands/minify.d.ts.map +1 -0
- package/dist/commands/minify.js +129 -0
- package/dist/commands/minify.js.map +1 -0
- package/dist/commands/music.d.ts +13 -0
- package/dist/commands/music.d.ts.map +1 -0
- package/dist/commands/music.js +241 -0
- package/dist/commands/music.js.map +1 -0
- package/dist/commands/playtest.d.ts +29 -0
- package/dist/commands/playtest.d.ts.map +1 -0
- package/dist/commands/playtest.js +631 -0
- package/dist/commands/playtest.js.map +1 -0
- package/dist/commands/run.d.ts +30 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +202 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/serve.d.ts +50 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +213 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/sfx.d.ts +13 -0
- package/dist/commands/sfx.d.ts.map +1 -0
- package/dist/commands/sfx.js +254 -0
- package/dist/commands/sfx.js.map +1 -0
- package/dist/commands/tokens.d.ts +36 -0
- package/dist/commands/tokens.d.ts.map +1 -0
- package/dist/commands/tokens.js +129 -0
- package/dist/commands/tokens.js.map +1 -0
- package/dist/commands/verify.d.ts +57 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +213 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/version.d.ts +12 -0
- package/dist/commands/version.d.ts.map +1 -0
- package/dist/commands/version.js +24 -0
- package/dist/commands/version.js.map +1 -0
- package/dist/engine/audio/index.d.ts +14 -0
- package/dist/engine/audio/index.d.ts.map +1 -0
- package/dist/engine/audio/index.js +14 -0
- package/dist/engine/audio/index.js.map +1 -0
- package/dist/engine/audio/music.d.ts +116 -0
- package/dist/engine/audio/music.d.ts.map +1 -0
- package/dist/engine/audio/music.js +138 -0
- package/dist/engine/audio/music.js.map +1 -0
- package/dist/engine/audio/section.d.ts +34 -0
- package/dist/engine/audio/section.d.ts.map +1 -0
- package/dist/engine/audio/section.js +67 -0
- package/dist/engine/audio/section.js.map +1 -0
- package/dist/engine/audio/sfx.d.ts +141 -0
- package/dist/engine/audio/sfx.d.ts.map +1 -0
- package/dist/engine/audio/sfx.js +506 -0
- package/dist/engine/audio/sfx.js.map +1 -0
- package/dist/engine/cart/cart.d.ts +75 -0
- package/dist/engine/cart/cart.d.ts.map +1 -0
- package/dist/engine/cart/cart.js +153 -0
- package/dist/engine/cart/cart.js.map +1 -0
- package/dist/engine/cart/errors.d.ts +29 -0
- package/dist/engine/cart/errors.d.ts.map +1 -0
- package/dist/engine/cart/errors.js +26 -0
- package/dist/engine/cart/errors.js.map +1 -0
- package/dist/engine/cart/gfx.d.ts +79 -0
- package/dist/engine/cart/gfx.d.ts.map +1 -0
- package/dist/engine/cart/gfx.js +171 -0
- package/dist/engine/cart/gfx.js.map +1 -0
- package/dist/engine/cart/index.d.ts +15 -0
- package/dist/engine/cart/index.d.ts.map +1 -0
- package/dist/engine/cart/index.js +15 -0
- package/dist/engine/cart/index.js.map +1 -0
- package/dist/engine/cart/map.d.ts +46 -0
- package/dist/engine/cart/map.d.ts.map +1 -0
- package/dist/engine/cart/map.js +118 -0
- package/dist/engine/cart/map.js.map +1 -0
- package/dist/engine/config/config.d.ts +74 -0
- package/dist/engine/config/config.d.ts.map +1 -0
- package/dist/engine/config/config.js +103 -0
- package/dist/engine/config/config.js.map +1 -0
- package/dist/engine/config/index.d.ts +7 -0
- package/dist/engine/config/index.d.ts.map +1 -0
- package/dist/engine/config/index.js +7 -0
- package/dist/engine/config/index.js.map +1 -0
- package/dist/engine/gfx/grid.d.ts +51 -0
- package/dist/engine/gfx/grid.d.ts.map +1 -0
- package/dist/engine/gfx/grid.js +116 -0
- package/dist/engine/gfx/grid.js.map +1 -0
- package/dist/engine/gfx/index.d.ts +17 -0
- package/dist/engine/gfx/index.d.ts.map +1 -0
- package/dist/engine/gfx/index.js +17 -0
- package/dist/engine/gfx/index.js.map +1 -0
- package/dist/engine/gfx/label.d.ts +34 -0
- package/dist/engine/gfx/label.d.ts.map +1 -0
- package/dist/engine/gfx/label.js +190 -0
- package/dist/engine/gfx/label.js.map +1 -0
- package/dist/engine/gfx/overlap.d.ts +42 -0
- package/dist/engine/gfx/overlap.d.ts.map +1 -0
- package/dist/engine/gfx/overlap.js +57 -0
- package/dist/engine/gfx/overlap.js.map +1 -0
- package/dist/engine/gfx/palette.d.ts +29 -0
- package/dist/engine/gfx/palette.d.ts.map +1 -0
- package/dist/engine/gfx/palette.js +46 -0
- package/dist/engine/gfx/palette.js.map +1 -0
- package/dist/engine/gfx/png.d.ts +31 -0
- package/dist/engine/gfx/png.d.ts.map +1 -0
- package/dist/engine/gfx/png.js +165 -0
- package/dist/engine/gfx/png.js.map +1 -0
- package/dist/engine/gfx/render.d.ts +42 -0
- package/dist/engine/gfx/render.d.ts.map +1 -0
- package/dist/engine/gfx/render.js +72 -0
- package/dist/engine/gfx/render.js.map +1 -0
- package/dist/engine/index.d.ts +22 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +22 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/pico8/adapter.d.ts +267 -0
- package/dist/engine/pico8/adapter.d.ts.map +1 -0
- package/dist/engine/pico8/adapter.js +37 -0
- package/dist/engine/pico8/adapter.js.map +1 -0
- package/dist/engine/pico8/drive.d.ts +308 -0
- package/dist/engine/pico8/drive.d.ts.map +1 -0
- package/dist/engine/pico8/drive.js +581 -0
- package/dist/engine/pico8/drive.js.map +1 -0
- package/dist/engine/pico8/harness.d.ts +146 -0
- package/dist/engine/pico8/harness.d.ts.map +1 -0
- package/dist/engine/pico8/harness.js +251 -0
- package/dist/engine/pico8/harness.js.map +1 -0
- package/dist/engine/pico8/index.d.ts +21 -0
- package/dist/engine/pico8/index.d.ts.map +1 -0
- package/dist/engine/pico8/index.js +21 -0
- package/dist/engine/pico8/index.js.map +1 -0
- package/dist/engine/pico8/sentinel.d.ts +63 -0
- package/dist/engine/pico8/sentinel.d.ts.map +1 -0
- package/dist/engine/pico8/sentinel.js +86 -0
- package/dist/engine/pico8/sentinel.js.map +1 -0
- package/dist/engine/pico8/session-daemon-entry.d.ts +10 -0
- package/dist/engine/pico8/session-daemon-entry.d.ts.map +1 -0
- package/dist/engine/pico8/session-daemon-entry.js +11 -0
- package/dist/engine/pico8/session-daemon-entry.js.map +1 -0
- package/dist/engine/pico8/session-daemon-main.d.ts +44 -0
- package/dist/engine/pico8/session-daemon-main.d.ts.map +1 -0
- package/dist/engine/pico8/session-daemon-main.js +71 -0
- package/dist/engine/pico8/session-daemon-main.js.map +1 -0
- package/dist/engine/pico8/session-daemon.d.ts +68 -0
- package/dist/engine/pico8/session-daemon.d.ts.map +1 -0
- package/dist/engine/pico8/session-daemon.js +264 -0
- package/dist/engine/pico8/session-daemon.js.map +1 -0
- package/dist/engine/pico8/session.d.ts +140 -0
- package/dist/engine/pico8/session.d.ts.map +1 -0
- package/dist/engine/pico8/session.js +250 -0
- package/dist/engine/pico8/session.js.map +1 -0
- package/dist/engine/pico8/shell.d.ts +103 -0
- package/dist/engine/pico8/shell.d.ts.map +1 -0
- package/dist/engine/pico8/shell.js +324 -0
- package/dist/engine/pico8/shell.js.map +1 -0
- package/dist/engine/pico8/supervisor.d.ts +137 -0
- package/dist/engine/pico8/supervisor.d.ts.map +1 -0
- package/dist/engine/pico8/supervisor.js +140 -0
- package/dist/engine/pico8/supervisor.js.map +1 -0
- package/dist/engine/scaffold/index.d.ts +8 -0
- package/dist/engine/scaffold/index.d.ts.map +1 -0
- package/dist/engine/scaffold/index.js +8 -0
- package/dist/engine/scaffold/index.js.map +1 -0
- package/dist/engine/scaffold/scaffold.d.ts +49 -0
- package/dist/engine/scaffold/scaffold.d.ts.map +1 -0
- package/dist/engine/scaffold/scaffold.js +217 -0
- package/dist/engine/scaffold/scaffold.js.map +1 -0
- package/dist/engine/shrinko/adapter.d.ts +167 -0
- package/dist/engine/shrinko/adapter.d.ts.map +1 -0
- package/dist/engine/shrinko/adapter.js +107 -0
- package/dist/engine/shrinko/adapter.js.map +1 -0
- package/dist/engine/shrinko/index.d.ts +13 -0
- package/dist/engine/shrinko/index.d.ts.map +1 -0
- package/dist/engine/shrinko/index.js +13 -0
- package/dist/engine/shrinko/index.js.map +1 -0
- package/dist/engine/shrinko/shell.d.ts +69 -0
- package/dist/engine/shrinko/shell.d.ts.map +1 -0
- package/dist/engine/shrinko/shell.js +156 -0
- package/dist/engine/shrinko/shell.js.map +1 -0
- package/dist/engine/skills/index.d.ts +8 -0
- package/dist/engine/skills/index.d.ts.map +1 -0
- package/dist/engine/skills/index.js +8 -0
- package/dist/engine/skills/index.js.map +1 -0
- package/dist/engine/skills/skills.d.ts +104 -0
- package/dist/engine/skills/skills.d.ts.map +1 -0
- package/dist/engine/skills/skills.js +149 -0
- package/dist/engine/skills/skills.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +19 -0
- package/dist/version.js.map +1 -0
- package/package.json +48 -2
- package/src/bin.ts +4 -0
- package/src/cli.ts +61 -0
- package/src/commands/audio.test.ts +280 -0
- package/src/commands/audio.ts +410 -0
- package/src/commands/export.test.ts +310 -0
- package/src/commands/export.ts +316 -0
- package/src/commands/gfx-io.test.ts +178 -0
- package/src/commands/gfx.test.ts +470 -0
- package/src/commands/gfx.ts +645 -0
- package/src/commands/init.test.ts +460 -0
- package/src/commands/init.ts +193 -0
- package/src/commands/lint.test.ts +137 -0
- package/src/commands/lint.ts +117 -0
- package/src/commands/minify.test.ts +166 -0
- package/src/commands/minify.ts +156 -0
- package/src/commands/music.test.ts +272 -0
- package/src/commands/music.ts +288 -0
- package/src/commands/playtest.test.ts +317 -0
- package/src/commands/playtest.ts +773 -0
- package/src/commands/run.test.ts +238 -0
- package/src/commands/run.ts +259 -0
- package/src/commands/serve.test.ts +218 -0
- package/src/commands/serve.ts +272 -0
- package/src/commands/sfx.test.ts +349 -0
- package/src/commands/sfx.ts +300 -0
- package/src/commands/tokens.test.ts +212 -0
- package/src/commands/tokens.ts +168 -0
- package/src/commands/verify.test.ts +238 -0
- package/src/commands/verify.ts +256 -0
- package/src/commands/version.test.ts +70 -0
- package/src/commands/version.ts +24 -0
- package/src/engine/audio/index.ts +47 -0
- package/src/engine/audio/music.test.ts +188 -0
- package/src/engine/audio/music.ts +218 -0
- package/src/engine/audio/section.test.ts +124 -0
- package/src/engine/audio/section.ts +79 -0
- package/src/engine/audio/sfx.test.ts +410 -0
- package/src/engine/audio/sfx.ts +694 -0
- package/src/engine/cart/cart.test.ts +304 -0
- package/src/engine/cart/cart.ts +211 -0
- package/src/engine/cart/errors.ts +39 -0
- package/src/engine/cart/fixtures/crlf.p8 +6 -0
- package/src/engine/cart/fixtures/full.p8 +174 -0
- package/src/engine/cart/fixtures/header-only.p8 +2 -0
- package/src/engine/cart/fixtures/malformed-no-header.p8 +2 -0
- package/src/engine/cart/fixtures/minimal.p8 +10 -0
- package/src/engine/cart/fixtures/no-trailing-newline.p8 +6 -0
- package/src/engine/cart/gfx.ts +195 -0
- package/src/engine/cart/index.ts +27 -0
- package/src/engine/cart/map.ts +134 -0
- package/src/engine/config/config.ts +115 -0
- package/src/engine/config/index.ts +12 -0
- package/src/engine/gfx/grid.test.ts +128 -0
- package/src/engine/gfx/grid.ts +136 -0
- package/src/engine/gfx/index.ts +34 -0
- package/src/engine/gfx/label.test.ts +105 -0
- package/src/engine/gfx/label.ts +229 -0
- package/src/engine/gfx/overlap.test.ts +71 -0
- package/src/engine/gfx/overlap.ts +66 -0
- package/src/engine/gfx/palette.test.ts +35 -0
- package/src/engine/gfx/palette.ts +54 -0
- package/src/engine/gfx/png.test.ts +111 -0
- package/src/engine/gfx/png.ts +196 -0
- package/src/engine/gfx/render.test.ts +180 -0
- package/src/engine/gfx/render.ts +106 -0
- package/src/engine/index.ts +21 -0
- package/src/engine/pico8/adapter.ts +303 -0
- package/src/engine/pico8/drive.test.ts +415 -0
- package/src/engine/pico8/drive.ts +729 -0
- package/src/engine/pico8/harness.test.ts +181 -0
- package/src/engine/pico8/harness.ts +354 -0
- package/src/engine/pico8/index.ts +20 -0
- package/src/engine/pico8/sentinel.test.ts +80 -0
- package/src/engine/pico8/sentinel.ts +93 -0
- package/src/engine/pico8/session-daemon-entry.ts +12 -0
- package/src/engine/pico8/session-daemon-main.ts +98 -0
- package/src/engine/pico8/session-daemon.test.ts +212 -0
- package/src/engine/pico8/session-daemon.ts +306 -0
- package/src/engine/pico8/session.test.ts +239 -0
- package/src/engine/pico8/session.ts +321 -0
- package/src/engine/pico8/shell.test.ts +583 -0
- package/src/engine/pico8/shell.ts +412 -0
- package/src/engine/pico8/supervisor.test.ts +105 -0
- package/src/engine/pico8/supervisor.ts +197 -0
- package/src/engine/scaffold/index.ts +15 -0
- package/src/engine/scaffold/scaffold.ts +236 -0
- package/src/engine/shrinko/adapter.ts +241 -0
- package/src/engine/shrinko/fixtures/count-no-compressed.txt +2 -0
- package/src/engine/shrinko/fixtures/count-over-budget.txt +3 -0
- package/src/engine/shrinko/fixtures/count-under-budget.txt +3 -0
- package/src/engine/shrinko/index.ts +33 -0
- package/src/engine/shrinko/shell.ts +224 -0
- package/src/engine/shrinko/shrinko.test.ts +182 -0
- package/src/engine/skills/index.ts +14 -0
- package/src/engine/skills/skills.test.ts +376 -0
- package/src/engine/skills/skills.ts +205 -0
- package/src/index.ts +13 -0
- package/src/skills/game-design-reference/SKILL.md +70 -0
- package/src/skills/game-jam/SKILL.md +45 -0
- package/src/skills/picopilot-art/SKILL.md +62 -0
- package/src/skills/picopilot-audio/SKILL.md +194 -0
- package/src/skills/picopilot-code/SKILL.md +97 -0
- package/src/skills/picopilot-code/reference/README.md +42 -0
- package/src/skills/picopilot-code/reference/mode7-racing.md +95 -0
- package/src/skills/picopilot-code/reference/platformer.md +119 -0
- package/src/skills/picopilot-code/reference/puzzle-grid.md +106 -0
- package/src/skills/picopilot-code/reference/rpg-menus-dialog.md +122 -0
- package/src/skills/picopilot-code/reference/top-down-adventure.md +117 -0
- package/src/skills/picopilot-code/reference/twin-stick-arcade.md +128 -0
- package/src/skills/picopilot-debug/SKILL.md +195 -0
- package/src/skills/picopilot-overview/SKILL.md +52 -0
- package/src/version.ts +22 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `__label__` builder: turn a 128x128 image into the hex a `.p8` cart's
|
|
3
|
+
* label section holds, so `picopilot export --label <png>` can give a labelless
|
|
4
|
+
* cart a loading splash WITHOUT an interactive PICO-8 (F7) capture.
|
|
5
|
+
*
|
|
6
|
+
* A PICO-8 label is a 128x128 grid of palette indices (0..15), serialised as 128
|
|
7
|
+
* rows of 128 hex nibbles. This module decodes a PNG to RGB (via Node's stdlib
|
|
8
|
+
* `zlib` inflate, no npm dependency), maps each pixel to the NEAREST PICO-8
|
|
9
|
+
* palette colour, and emits that hex. It is the inverse direction of the
|
|
10
|
+
* palette-accurate PNG RENDER in `render.ts`/`png.ts` (which goes indices -> RGB).
|
|
11
|
+
*
|
|
12
|
+
* It intentionally supports only the PNG subset a caller realistically produces
|
|
13
|
+
* for a 128x128 label: 8-bit, non-interlaced, colour types 2 (RGB) and 6 (RGBA),
|
|
14
|
+
* and (via the palette map) 0/3 are rejected with a clear error rather than
|
|
15
|
+
* guessed at. Anything else fails loud with a structured {@link LabelError}.
|
|
16
|
+
*/
|
|
17
|
+
import { inflateSync } from 'node:zlib';
|
|
18
|
+
import { PICO8_PALETTE } from './palette.js';
|
|
19
|
+
/** The fixed PICO-8 label dimensions: a 128x128 image. */
|
|
20
|
+
export const LABEL_SIZE = 128;
|
|
21
|
+
/** A structured label-build failure (bad PNG, wrong size, unsupported format). */
|
|
22
|
+
export class LabelError extends Error {
|
|
23
|
+
code;
|
|
24
|
+
constructor(code, message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.code = code;
|
|
27
|
+
this.name = 'LabelError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
|
|
31
|
+
/**
|
|
32
|
+
* Decodes an 8-bit, non-interlaced PNG (colour type 2 RGB or 6 RGBA) to RGBA.
|
|
33
|
+
* Uses `node:zlib` inflate for the IDAT stream and applies the five PNG scanline
|
|
34
|
+
* filters. Throws {@link LabelError} for anything outside this subset.
|
|
35
|
+
*/
|
|
36
|
+
function decodePng(bytes) {
|
|
37
|
+
for (let i = 0; i < PNG_SIGNATURE.length; i++) {
|
|
38
|
+
if (bytes[i] !== PNG_SIGNATURE[i]) {
|
|
39
|
+
throw new LabelError('not-a-png', 'not a PNG file (bad signature)');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
let off = 8;
|
|
43
|
+
let width = 0;
|
|
44
|
+
let height = 0;
|
|
45
|
+
let bitDepth = 0;
|
|
46
|
+
let colourType = 0;
|
|
47
|
+
let interlace = 0;
|
|
48
|
+
const idat = [];
|
|
49
|
+
const readU32 = (p) => ((bytes[p] << 24) |
|
|
50
|
+
(bytes[p + 1] << 16) |
|
|
51
|
+
(bytes[p + 2] << 8) |
|
|
52
|
+
bytes[p + 3]) >>>
|
|
53
|
+
0;
|
|
54
|
+
while (off + 8 <= bytes.length) {
|
|
55
|
+
const len = readU32(off);
|
|
56
|
+
const type = String.fromCharCode(bytes[off + 4], bytes[off + 5], bytes[off + 6], bytes[off + 7]);
|
|
57
|
+
const dataStart = off + 8;
|
|
58
|
+
const dataEnd = dataStart + len;
|
|
59
|
+
if (dataEnd + 4 > bytes.length) {
|
|
60
|
+
throw new LabelError('truncated-png', 'PNG chunk runs past end of file');
|
|
61
|
+
}
|
|
62
|
+
if (type === 'IHDR') {
|
|
63
|
+
width = readU32(dataStart);
|
|
64
|
+
height = readU32(dataStart + 4);
|
|
65
|
+
bitDepth = bytes[dataStart + 8];
|
|
66
|
+
colourType = bytes[dataStart + 9];
|
|
67
|
+
interlace = bytes[dataStart + 12];
|
|
68
|
+
}
|
|
69
|
+
else if (type === 'IDAT') {
|
|
70
|
+
idat.push(bytes.subarray(dataStart, dataEnd));
|
|
71
|
+
}
|
|
72
|
+
else if (type === 'IEND') {
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
off = dataEnd + 4; // skip the 4-byte CRC
|
|
76
|
+
}
|
|
77
|
+
if (bitDepth !== 8 ||
|
|
78
|
+
interlace !== 0 ||
|
|
79
|
+
(colourType !== 2 && colourType !== 6)) {
|
|
80
|
+
throw new LabelError('unsupported-png', `unsupported PNG: need 8-bit non-interlaced RGB or RGBA (got bitDepth=${bitDepth}, colourType=${colourType}, interlace=${interlace})`);
|
|
81
|
+
}
|
|
82
|
+
const channels = colourType === 6 ? 4 : 3;
|
|
83
|
+
const compressed = Buffer.concat(idat.map((c) => Buffer.from(c)));
|
|
84
|
+
let raw;
|
|
85
|
+
try {
|
|
86
|
+
raw = inflateSync(compressed);
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
throw new LabelError('truncated-png', `could not inflate PNG image data: ${e.message}`);
|
|
90
|
+
}
|
|
91
|
+
const stride = width * channels;
|
|
92
|
+
const rgba = new Uint8Array(width * height * 4);
|
|
93
|
+
const prev = new Uint8Array(stride);
|
|
94
|
+
let src = 0;
|
|
95
|
+
for (let y = 0; y < height; y++) {
|
|
96
|
+
const filter = raw[src++];
|
|
97
|
+
const line = new Uint8Array(stride);
|
|
98
|
+
for (let x = 0; x < stride; x++) {
|
|
99
|
+
const rawByte = raw[src++];
|
|
100
|
+
const a = x >= channels ? line[x - channels] : 0; // left
|
|
101
|
+
const b = prev[x]; // up
|
|
102
|
+
const c = x >= channels ? prev[x - channels] : 0; // up-left
|
|
103
|
+
let val = rawByte;
|
|
104
|
+
switch (filter) {
|
|
105
|
+
case 0:
|
|
106
|
+
break; // None
|
|
107
|
+
case 1:
|
|
108
|
+
val = (rawByte + a) & 0xff;
|
|
109
|
+
break; // Sub
|
|
110
|
+
case 2:
|
|
111
|
+
val = (rawByte + b) & 0xff;
|
|
112
|
+
break; // Up
|
|
113
|
+
case 3:
|
|
114
|
+
val = (rawByte + ((a + b) >> 1)) & 0xff;
|
|
115
|
+
break; // Average
|
|
116
|
+
case 4:
|
|
117
|
+
val = (rawByte + paeth(a, b, c)) & 0xff;
|
|
118
|
+
break; // Paeth
|
|
119
|
+
default:
|
|
120
|
+
throw new LabelError('unsupported-png', `unsupported PNG scanline filter ${filter}`);
|
|
121
|
+
}
|
|
122
|
+
line[x] = val;
|
|
123
|
+
}
|
|
124
|
+
// Expand this decoded scanline into RGBA.
|
|
125
|
+
for (let x = 0; x < width; x++) {
|
|
126
|
+
const s = x * channels;
|
|
127
|
+
const d = (y * width + x) * 4;
|
|
128
|
+
rgba[d] = line[s];
|
|
129
|
+
rgba[d + 1] = line[s + 1];
|
|
130
|
+
rgba[d + 2] = line[s + 2];
|
|
131
|
+
rgba[d + 3] = channels === 4 ? line[s + 3] : 255;
|
|
132
|
+
}
|
|
133
|
+
prev.set(line);
|
|
134
|
+
}
|
|
135
|
+
return { width, height, rgba };
|
|
136
|
+
}
|
|
137
|
+
/** The PNG Paeth predictor. */
|
|
138
|
+
function paeth(a, b, c) {
|
|
139
|
+
const p = a + b - c;
|
|
140
|
+
const pa = Math.abs(p - a);
|
|
141
|
+
const pb = Math.abs(p - b);
|
|
142
|
+
const pc = Math.abs(p - c);
|
|
143
|
+
if (pa <= pb && pa <= pc)
|
|
144
|
+
return a;
|
|
145
|
+
if (pb <= pc)
|
|
146
|
+
return b;
|
|
147
|
+
return c;
|
|
148
|
+
}
|
|
149
|
+
/** The nearest PICO-8 palette index (by squared RGB distance) for an RGB triple. */
|
|
150
|
+
export function nearestPaletteIndex(r, g, b) {
|
|
151
|
+
let best = 0;
|
|
152
|
+
let bestDist = Infinity;
|
|
153
|
+
for (let i = 0; i < PICO8_PALETTE.length; i++) {
|
|
154
|
+
const c = PICO8_PALETTE[i];
|
|
155
|
+
const dr = r - c.r;
|
|
156
|
+
const dg = g - c.g;
|
|
157
|
+
const db = b - c.b;
|
|
158
|
+
const dist = dr * dr + dg * dg + db * db;
|
|
159
|
+
if (dist < bestDist) {
|
|
160
|
+
bestDist = dist;
|
|
161
|
+
best = i;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return best;
|
|
165
|
+
}
|
|
166
|
+
const HEX = '0123456789abcdef';
|
|
167
|
+
/**
|
|
168
|
+
* Builds the `__label__` hex body (128 rows of 128 nibbles, newline-joined, with
|
|
169
|
+
* a trailing newline) from a 128x128 PNG. Each pixel is mapped to its nearest
|
|
170
|
+
* PICO-8 palette colour. The PNG MUST be exactly 128x128 ({@link LABEL_SIZE});
|
|
171
|
+
* anything else is a {@link LabelError} (`wrong-size`) so a mis-sized image fails
|
|
172
|
+
* loud instead of being silently cropped or stretched.
|
|
173
|
+
*/
|
|
174
|
+
export function labelHexFromPng(pngBytes) {
|
|
175
|
+
const { width, height, rgba } = decodePng(pngBytes);
|
|
176
|
+
if (width !== LABEL_SIZE || height !== LABEL_SIZE) {
|
|
177
|
+
throw new LabelError('wrong-size', `label image must be ${LABEL_SIZE}x${LABEL_SIZE}, got ${width}x${height}`);
|
|
178
|
+
}
|
|
179
|
+
const rows = [];
|
|
180
|
+
for (let y = 0; y < LABEL_SIZE; y++) {
|
|
181
|
+
let row = '';
|
|
182
|
+
for (let x = 0; x < LABEL_SIZE; x++) {
|
|
183
|
+
const d = (y * LABEL_SIZE + x) * 4;
|
|
184
|
+
row += HEX[nearestPaletteIndex(rgba[d], rgba[d + 1], rgba[d + 2])];
|
|
185
|
+
}
|
|
186
|
+
rows.push(row);
|
|
187
|
+
}
|
|
188
|
+
return `${rows.join('\n')}\n`;
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=label.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"label.js","sourceRoot":"","sources":["../../../src/engine/gfx/label.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type GfxSheet } from '../cart/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* The gfx/map overlap smart-refuse decision (ADR-0004), computed purely from a
|
|
4
|
+
* cart's CURRENT `__gfx__` pixels plus the authorisation flag.
|
|
5
|
+
*
|
|
6
|
+
* Why `__gfx__`, not `__map__` (the corrected seam): sprites 128..255 alias the
|
|
7
|
+
* shared map region, but those shared bytes live in the `__gfx__` `0x1000` upper
|
|
8
|
+
* bank, NOT in the text-format `.p8` `__map__` section (which stores only map
|
|
9
|
+
* rows 0..31). So "the data at risk" when overwriting a shared-bank sprite IS
|
|
10
|
+
* the sprite's OWN current `__gfx__` pixels. This module inspects those pixels
|
|
11
|
+
* directly (via {@link GfxSheet}); it deliberately does NOT consult `__map__`.
|
|
12
|
+
*
|
|
13
|
+
* The single invariant: picopilot never SILENTLY overwrites existing
|
|
14
|
+
* shared-region data that nothing authorised.
|
|
15
|
+
*/
|
|
16
|
+
/** Whether ANY pixel of sprite `n` is non-zero, i.e. the sprite already holds data. */
|
|
17
|
+
export declare function spriteHasData(sheet: GfxSheet, n: number): boolean;
|
|
18
|
+
/** The verdict of {@link decideOverlap}. */
|
|
19
|
+
export type OverlapDecision = {
|
|
20
|
+
kind: 'allowed';
|
|
21
|
+
} | {
|
|
22
|
+
kind: 'allowed-shared';
|
|
23
|
+
reason: 'authorised' | 'empty';
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'refused';
|
|
26
|
+
nonZeroPixels: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Decides whether a `gfx set` write to sprite `n` may proceed, given the cart's
|
|
30
|
+
* current `__gfx__` and whether overlap is authorised.
|
|
31
|
+
*
|
|
32
|
+
* - sprite 0..127 (base bank, aliases nothing) → `allowed` (no overlap possible).
|
|
33
|
+
* - sprite 128..255 whose current pixels are ALL-ZERO → `allowed-shared` (empty):
|
|
34
|
+
* the write proceeds; the caller notes the aliasing in its result.
|
|
35
|
+
* - sprite 128..255 with non-zero pixels AND `authorised` → `allowed-shared`
|
|
36
|
+
* (authorised): the write proceeds and overwrites the shared data on purpose.
|
|
37
|
+
* - sprite 128..255 with non-zero pixels AND NOT authorised → `refused`: the
|
|
38
|
+
* genuine data-loss corner. The caller REFUSES (structured + nonzero exit +
|
|
39
|
+
* bytes untouched).
|
|
40
|
+
*/
|
|
41
|
+
export declare function decideOverlap(sheet: GfxSheet, n: number, authorised: boolean): OverlapDecision;
|
|
42
|
+
//# sourceMappingURL=overlap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlap.d.ts","sourceRoot":"","sources":["../../../src/engine/gfx/overlap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,QAAQ,EAAmB,MAAM,kBAAkB,CAAC;AAEjE;;;;;;;;;;;;;GAaG;AAEH,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAMjE;AAED,4CAA4C;AAC5C,MAAM,MAAM,eAAe,GACxB;IAAC,IAAI,EAAE,SAAS,CAAA;CAAC,GACjB;IAAC,IAAI,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAA;CAAC,GACxD;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAC,CAAC;AAE5C;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC5B,KAAK,EAAE,QAAQ,EACf,CAAC,EAAE,MAAM,EACT,UAAU,EAAE,OAAO,GACjB,eAAe,CAQjB"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { spriteAliasesMap } from '../cart/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* The gfx/map overlap smart-refuse decision (ADR-0004), computed purely from a
|
|
4
|
+
* cart's CURRENT `__gfx__` pixels plus the authorisation flag.
|
|
5
|
+
*
|
|
6
|
+
* Why `__gfx__`, not `__map__` (the corrected seam): sprites 128..255 alias the
|
|
7
|
+
* shared map region, but those shared bytes live in the `__gfx__` `0x1000` upper
|
|
8
|
+
* bank, NOT in the text-format `.p8` `__map__` section (which stores only map
|
|
9
|
+
* rows 0..31). So "the data at risk" when overwriting a shared-bank sprite IS
|
|
10
|
+
* the sprite's OWN current `__gfx__` pixels. This module inspects those pixels
|
|
11
|
+
* directly (via {@link GfxSheet}); it deliberately does NOT consult `__map__`.
|
|
12
|
+
*
|
|
13
|
+
* The single invariant: picopilot never SILENTLY overwrites existing
|
|
14
|
+
* shared-region data that nothing authorised.
|
|
15
|
+
*/
|
|
16
|
+
/** Whether ANY pixel of sprite `n` is non-zero, i.e. the sprite already holds data. */
|
|
17
|
+
export function spriteHasData(sheet, n) {
|
|
18
|
+
const grid = sheet.getSprite(n);
|
|
19
|
+
for (const row of grid) {
|
|
20
|
+
for (const px of row)
|
|
21
|
+
if (px !== 0)
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Decides whether a `gfx set` write to sprite `n` may proceed, given the cart's
|
|
28
|
+
* current `__gfx__` and whether overlap is authorised.
|
|
29
|
+
*
|
|
30
|
+
* - sprite 0..127 (base bank, aliases nothing) → `allowed` (no overlap possible).
|
|
31
|
+
* - sprite 128..255 whose current pixels are ALL-ZERO → `allowed-shared` (empty):
|
|
32
|
+
* the write proceeds; the caller notes the aliasing in its result.
|
|
33
|
+
* - sprite 128..255 with non-zero pixels AND `authorised` → `allowed-shared`
|
|
34
|
+
* (authorised): the write proceeds and overwrites the shared data on purpose.
|
|
35
|
+
* - sprite 128..255 with non-zero pixels AND NOT authorised → `refused`: the
|
|
36
|
+
* genuine data-loss corner. The caller REFUSES (structured + nonzero exit +
|
|
37
|
+
* bytes untouched).
|
|
38
|
+
*/
|
|
39
|
+
export function decideOverlap(sheet, n, authorised) {
|
|
40
|
+
if (!spriteAliasesMap(n))
|
|
41
|
+
return { kind: 'allowed' };
|
|
42
|
+
if (!spriteHasData(sheet, n))
|
|
43
|
+
return { kind: 'allowed-shared', reason: 'empty' };
|
|
44
|
+
if (authorised)
|
|
45
|
+
return { kind: 'allowed-shared', reason: 'authorised' };
|
|
46
|
+
return { kind: 'refused', nonZeroPixels: countNonZeroPixels(sheet, n) };
|
|
47
|
+
}
|
|
48
|
+
function countNonZeroPixels(sheet, n) {
|
|
49
|
+
let count = 0;
|
|
50
|
+
for (const row of sheet.getSprite(n)) {
|
|
51
|
+
for (const px of row)
|
|
52
|
+
if (px !== 0)
|
|
53
|
+
count++;
|
|
54
|
+
}
|
|
55
|
+
return count;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=overlap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlap.js","sourceRoot":"","sources":["../../../src/engine/gfx/overlap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AAEjE;;;;;;;;;;;;;GAaG;AAEH,uFAAuF;AACvF,MAAM,UAAU,aAAa,CAAC,KAAe,EAAE,CAAS;IACvD,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAChC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACxB,KAAK,MAAM,EAAE,IAAI,GAAG;YAAE,IAAI,EAAE,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;IACjD,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAQD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,aAAa,CAC5B,KAAe,EACf,CAAS,EACT,UAAmB;IAEnB,IAAI,CAAC,gBAAgB,CAAC,CAAC,CAAC;QAAE,OAAO,EAAC,IAAI,EAAE,SAAS,EAAC,CAAC;IAEnD,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3B,OAAO,EAAC,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAC,CAAC;IAClD,IAAI,UAAU;QAAE,OAAO,EAAC,IAAI,EAAE,gBAAgB,EAAE,MAAM,EAAE,YAAY,EAAC,CAAC;IAEtE,OAAO,EAAC,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,kBAAkB,CAAC,KAAK,EAAE,CAAC,CAAC,EAAC,CAAC;AACvE,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAe,EAAE,CAAS;IACrD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;QACtC,KAAK,MAAM,EAAE,IAAI,GAAG;YAAE,IAAI,EAAE,KAAK,CAAC;gBAAE,KAAK,EAAE,CAAC;IAC7C,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PICO-8's FIXED 16-colour palette (index → RGB), the single source of truth for
|
|
3
|
+
* the `gfx render` PNG encoder.
|
|
4
|
+
*
|
|
5
|
+
* These are the stable, widely-reproduced PICO-8 0..15 palette constants,
|
|
6
|
+
* transcribed verbatim from `work/notes/findings/pico8-api-reference.md` (the
|
|
7
|
+
* same table the scaffolded `AGENTS.md` reference cites). Do NOT re-invent these
|
|
8
|
+
* values: if a future PICO-8 version alters them, revise the finding, the
|
|
9
|
+
* scaffold reference, and THIS table together.
|
|
10
|
+
*
|
|
11
|
+
* Index 0 doubles as PICO-8's default transparent colour; `gfx render` renders it
|
|
12
|
+
* as its RGB (black) so the PNG shows the actual pixels a `spr()` with no
|
|
13
|
+
* transparency would draw. (Transparency is a draw-time concern, not a property
|
|
14
|
+
* of the spritesheet bytes, so the JUDGE surface renders every index opaquely.)
|
|
15
|
+
*/
|
|
16
|
+
/** One palette entry: the colour index and its RGB triple. */
|
|
17
|
+
export interface PaletteColor {
|
|
18
|
+
readonly r: number;
|
|
19
|
+
readonly g: number;
|
|
20
|
+
readonly b: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The 16 PICO-8 palette RGB triples, indexed 0..15. Frozen so no caller can
|
|
24
|
+
* mutate the shared source of truth.
|
|
25
|
+
*/
|
|
26
|
+
export declare const PICO8_PALETTE: readonly PaletteColor[];
|
|
27
|
+
/** The RGB triple for colour index 0..15. Throws for an out-of-range index. */
|
|
28
|
+
export declare function paletteColor(index: number): PaletteColor;
|
|
29
|
+
//# sourceMappingURL=palette.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"palette.d.ts","sourceRoot":"","sources":["../../../src/engine/gfx/palette.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,8DAA8D;AAC9D,MAAM,WAAW,YAAY;IAC5B,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,YAAY,EAiBtC,CAAC;AAEZ,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAMxD"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PICO-8's FIXED 16-colour palette (index → RGB), the single source of truth for
|
|
3
|
+
* the `gfx render` PNG encoder.
|
|
4
|
+
*
|
|
5
|
+
* These are the stable, widely-reproduced PICO-8 0..15 palette constants,
|
|
6
|
+
* transcribed verbatim from `work/notes/findings/pico8-api-reference.md` (the
|
|
7
|
+
* same table the scaffolded `AGENTS.md` reference cites). Do NOT re-invent these
|
|
8
|
+
* values: if a future PICO-8 version alters them, revise the finding, the
|
|
9
|
+
* scaffold reference, and THIS table together.
|
|
10
|
+
*
|
|
11
|
+
* Index 0 doubles as PICO-8's default transparent colour; `gfx render` renders it
|
|
12
|
+
* as its RGB (black) so the PNG shows the actual pixels a `spr()` with no
|
|
13
|
+
* transparency would draw. (Transparency is a draw-time concern, not a property
|
|
14
|
+
* of the spritesheet bytes, so the JUDGE surface renders every index opaquely.)
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The 16 PICO-8 palette RGB triples, indexed 0..15. Frozen so no caller can
|
|
18
|
+
* mutate the shared source of truth.
|
|
19
|
+
*/
|
|
20
|
+
export const PICO8_PALETTE = Object.freeze([
|
|
21
|
+
{ r: 0, g: 0, b: 0 }, // 0 black
|
|
22
|
+
{ r: 29, g: 43, b: 83 }, // 1 dark-blue
|
|
23
|
+
{ r: 126, g: 37, b: 83 }, // 2 dark-purple
|
|
24
|
+
{ r: 0, g: 135, b: 81 }, // 3 dark-green
|
|
25
|
+
{ r: 171, g: 82, b: 54 }, // 4 brown
|
|
26
|
+
{ r: 95, g: 87, b: 79 }, // 5 dark-grey
|
|
27
|
+
{ r: 194, g: 195, b: 199 }, // 6 light-grey
|
|
28
|
+
{ r: 255, g: 241, b: 232 }, // 7 white
|
|
29
|
+
{ r: 255, g: 0, b: 77 }, // 8 red
|
|
30
|
+
{ r: 255, g: 163, b: 0 }, // 9 orange
|
|
31
|
+
{ r: 255, g: 236, b: 39 }, // 10 yellow
|
|
32
|
+
{ r: 0, g: 228, b: 54 }, // 11 green
|
|
33
|
+
{ r: 41, g: 173, b: 255 }, // 12 blue
|
|
34
|
+
{ r: 131, g: 118, b: 156 }, // 13 lavender
|
|
35
|
+
{ r: 255, g: 119, b: 168 }, // 14 pink
|
|
36
|
+
{ r: 255, g: 204, b: 170 }, // 15 peach
|
|
37
|
+
]);
|
|
38
|
+
/** The RGB triple for colour index 0..15. Throws for an out-of-range index. */
|
|
39
|
+
export function paletteColor(index) {
|
|
40
|
+
const c = PICO8_PALETTE[index];
|
|
41
|
+
if (c === undefined) {
|
|
42
|
+
throw new RangeError(`palette index ${index} out of range 0..15`);
|
|
43
|
+
}
|
|
44
|
+
return c;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=palette.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"palette.js","sourceRoot":"","sources":["../../../src/engine/gfx/palette.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AASH;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAA4B,MAAM,CAAC,MAAM,CAAC;IACnE,EAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAC,EAAE,WAAW;IAC/B,EAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAC,EAAE,eAAe;IACtC,EAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAC,EAAE,iBAAiB;IACzC,EAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAC,EAAE,gBAAgB;IACvC,EAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAC,EAAE,WAAW;IACnC,EAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAC,EAAE,eAAe;IACtC,EAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAC,EAAE,gBAAgB;IAC1C,EAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAC,EAAE,WAAW;IACrC,EAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAC,EAAE,SAAS;IAChC,EAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,EAAC,EAAE,YAAY;IACpC,EAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAC,EAAE,YAAY;IACrC,EAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAC,EAAE,WAAW;IAClC,EAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAC,EAAE,UAAU;IACnC,EAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAC,EAAE,cAAc;IACxC,EAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAC,EAAE,UAAU;IACpC,EAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAC,EAAE,WAAW;CAC5B,CAAC,CAAC;AAEZ,+EAA+E;AAC/E,MAAM,UAAU,YAAY,CAAC,KAAa;IACzC,MAAM,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;QACrB,MAAM,IAAI,UAAU,CAAC,iBAAiB,KAAK,qBAAqB,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,CAAC,CAAC;AACV,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A tiny, dependency-FREE, deterministic PNG encoder for the `gfx render` JUDGE
|
|
3
|
+
* surface. Encodes an RGB pixel buffer into a valid 8-bit truecolour PNG using
|
|
4
|
+
* hand-rolled CRC32, Adler32, and STORED (uncompressed) zlib deflate blocks.
|
|
5
|
+
*
|
|
6
|
+
* Why hand-rolled + stored blocks (recorded as a `gfx render` encoder choice):
|
|
7
|
+
* the eyes-loop is meant to stay dependency-light (shrinko-free, no npm PNG lib),
|
|
8
|
+
* and `gfx render` output is tiny (~256x256), so compression buys nothing worth a
|
|
9
|
+
* dependency. STORED deflate blocks keep the encoder ~a page of code AND make the
|
|
10
|
+
* output BYTE-DETERMINISTIC (no compressor heuristics), which is exactly what the
|
|
11
|
+
* pixel-byte tests assert against. A standard PNG decoder reads stored blocks
|
|
12
|
+
* identically to compressed ones, so the file is a normal, viewable PNG.
|
|
13
|
+
*
|
|
14
|
+
* This module knows nothing about PICO-8: it takes raw RGB and dimensions. The
|
|
15
|
+
* palette mapping + upscale live in `render.ts`.
|
|
16
|
+
*/
|
|
17
|
+
/** A raw RGB image: `width * height * 3` bytes, row-major, no padding. */
|
|
18
|
+
export interface RgbImage {
|
|
19
|
+
readonly width: number;
|
|
20
|
+
readonly height: number;
|
|
21
|
+
/** `rgb[(y * width + x) * 3 + {0,1,2}]` = R, G, B (0..255) for pixel (x, y). */
|
|
22
|
+
readonly rgb: Uint8Array;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Encodes an {@link RgbImage} into a complete PNG file (signature + IHDR + IDAT +
|
|
26
|
+
* IEND) as a `Uint8Array`. 8-bit RGB truecolour (colour type 2), no interlace,
|
|
27
|
+
* each scanline prefixed with filter byte 0 (None) so the pixel bytes appear
|
|
28
|
+
* verbatim in the IDAT payload.
|
|
29
|
+
*/
|
|
30
|
+
export declare function encodePng(image: RgbImage): Uint8Array;
|
|
31
|
+
//# sourceMappingURL=png.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"png.d.ts","sourceRoot":"","sources":["../../../src/engine/gfx/png.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,0EAA0E;AAC1E,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;CACzB;AAMD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,UAAU,CA+CrD"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A tiny, dependency-FREE, deterministic PNG encoder for the `gfx render` JUDGE
|
|
3
|
+
* surface. Encodes an RGB pixel buffer into a valid 8-bit truecolour PNG using
|
|
4
|
+
* hand-rolled CRC32, Adler32, and STORED (uncompressed) zlib deflate blocks.
|
|
5
|
+
*
|
|
6
|
+
* Why hand-rolled + stored blocks (recorded as a `gfx render` encoder choice):
|
|
7
|
+
* the eyes-loop is meant to stay dependency-light (shrinko-free, no npm PNG lib),
|
|
8
|
+
* and `gfx render` output is tiny (~256x256), so compression buys nothing worth a
|
|
9
|
+
* dependency. STORED deflate blocks keep the encoder ~a page of code AND make the
|
|
10
|
+
* output BYTE-DETERMINISTIC (no compressor heuristics), which is exactly what the
|
|
11
|
+
* pixel-byte tests assert against. A standard PNG decoder reads stored blocks
|
|
12
|
+
* identically to compressed ones, so the file is a normal, viewable PNG.
|
|
13
|
+
*
|
|
14
|
+
* This module knows nothing about PICO-8: it takes raw RGB and dimensions. The
|
|
15
|
+
* palette mapping + upscale live in `render.ts`.
|
|
16
|
+
*/
|
|
17
|
+
const PNG_SIGNATURE = Uint8Array.from([
|
|
18
|
+
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
|
|
19
|
+
]);
|
|
20
|
+
/**
|
|
21
|
+
* Encodes an {@link RgbImage} into a complete PNG file (signature + IHDR + IDAT +
|
|
22
|
+
* IEND) as a `Uint8Array`. 8-bit RGB truecolour (colour type 2), no interlace,
|
|
23
|
+
* each scanline prefixed with filter byte 0 (None) so the pixel bytes appear
|
|
24
|
+
* verbatim in the IDAT payload.
|
|
25
|
+
*/
|
|
26
|
+
export function encodePng(image) {
|
|
27
|
+
const { width, height, rgb } = image;
|
|
28
|
+
if (!Number.isInteger(width) ||
|
|
29
|
+
!Number.isInteger(height) ||
|
|
30
|
+
width <= 0 ||
|
|
31
|
+
height <= 0) {
|
|
32
|
+
throw new RangeError(`PNG dimensions must be positive integers, got ${width}x${height}`);
|
|
33
|
+
}
|
|
34
|
+
if (rgb.length !== width * height * 3) {
|
|
35
|
+
throw new RangeError(`RGB buffer length ${rgb.length} does not match ${width}x${height}x3 = ${width * height * 3}`);
|
|
36
|
+
}
|
|
37
|
+
// Raw scanlines: each row is one filter byte (0 = None) then width*3 RGB bytes.
|
|
38
|
+
const stride = width * 3;
|
|
39
|
+
const raw = new Uint8Array(height * (stride + 1));
|
|
40
|
+
for (let y = 0; y < height; y++) {
|
|
41
|
+
const dst = y * (stride + 1);
|
|
42
|
+
raw[dst] = 0; // filter type None
|
|
43
|
+
raw.set(rgb.subarray(y * stride, y * stride + stride), dst + 1);
|
|
44
|
+
}
|
|
45
|
+
const ihdr = buildIhdr(width, height);
|
|
46
|
+
const idat = zlibStore(raw);
|
|
47
|
+
const chunks = [
|
|
48
|
+
chunk('IHDR', ihdr),
|
|
49
|
+
chunk('IDAT', idat),
|
|
50
|
+
chunk('IEND', new Uint8Array(0)),
|
|
51
|
+
];
|
|
52
|
+
let total = PNG_SIGNATURE.length;
|
|
53
|
+
for (const c of chunks)
|
|
54
|
+
total += c.length;
|
|
55
|
+
const out = new Uint8Array(total);
|
|
56
|
+
let off = 0;
|
|
57
|
+
out.set(PNG_SIGNATURE, off);
|
|
58
|
+
off += PNG_SIGNATURE.length;
|
|
59
|
+
for (const c of chunks) {
|
|
60
|
+
out.set(c, off);
|
|
61
|
+
off += c.length;
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
/** The 13-byte IHDR data: width, height, bit-depth 8, colour type 2 (RGB), etc. */
|
|
66
|
+
function buildIhdr(width, height) {
|
|
67
|
+
const d = new Uint8Array(13);
|
|
68
|
+
writeUint32(d, 0, width);
|
|
69
|
+
writeUint32(d, 4, height);
|
|
70
|
+
d[8] = 8; // bit depth
|
|
71
|
+
d[9] = 2; // colour type: truecolour RGB
|
|
72
|
+
d[10] = 0; // compression: deflate
|
|
73
|
+
d[11] = 0; // filter: adaptive (only filter 0 used per scanline)
|
|
74
|
+
d[12] = 0; // interlace: none
|
|
75
|
+
return d;
|
|
76
|
+
}
|
|
77
|
+
/** Wraps chunk `data` with its 4-byte length, 4-byte type, and 4-byte CRC32. */
|
|
78
|
+
function chunk(type, data) {
|
|
79
|
+
const typeBytes = Uint8Array.from([
|
|
80
|
+
type.charCodeAt(0),
|
|
81
|
+
type.charCodeAt(1),
|
|
82
|
+
type.charCodeAt(2),
|
|
83
|
+
type.charCodeAt(3),
|
|
84
|
+
]);
|
|
85
|
+
const out = new Uint8Array(4 + 4 + data.length + 4);
|
|
86
|
+
writeUint32(out, 0, data.length);
|
|
87
|
+
out.set(typeBytes, 4);
|
|
88
|
+
out.set(data, 8);
|
|
89
|
+
// CRC covers the type + data (not the length).
|
|
90
|
+
const crcInput = new Uint8Array(4 + data.length);
|
|
91
|
+
crcInput.set(typeBytes, 0);
|
|
92
|
+
crcInput.set(data, 4);
|
|
93
|
+
writeUint32(out, 8 + data.length, crc32(crcInput));
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Wraps `data` in a zlib stream (RFC 1950) using DEFLATE STORED blocks (RFC 1951,
|
|
98
|
+
* BTYPE=00): a 2-byte zlib header, one or more stored blocks (each ≤ 65535
|
|
99
|
+
* bytes), and a 4-byte big-endian Adler32 of the uncompressed data.
|
|
100
|
+
*/
|
|
101
|
+
function zlibStore(data) {
|
|
102
|
+
const MAX_BLOCK = 0xffff;
|
|
103
|
+
const blockCount = Math.max(1, Math.ceil(data.length / MAX_BLOCK));
|
|
104
|
+
// 2 header bytes + per-block (1 BFINAL/BTYPE + 2 LEN + 2 NLEN) + data + 4 adler.
|
|
105
|
+
const out = new Uint8Array(2 + blockCount * 5 + data.length + 4);
|
|
106
|
+
let off = 0;
|
|
107
|
+
// zlib header: CMF=0x78 (deflate, 32K window), FLG=0x01 so (CMF*256+FLG)%31==0.
|
|
108
|
+
out[off++] = 0x78;
|
|
109
|
+
out[off++] = 0x01;
|
|
110
|
+
let pos = 0;
|
|
111
|
+
for (let b = 0; b < blockCount; b++) {
|
|
112
|
+
const len = Math.min(MAX_BLOCK, data.length - pos);
|
|
113
|
+
const isFinal = b === blockCount - 1;
|
|
114
|
+
out[off++] = isFinal ? 1 : 0; // BFINAL bit, BTYPE=00 (stored)
|
|
115
|
+
// LEN then NLEN, both little-endian.
|
|
116
|
+
out[off++] = len & 0xff;
|
|
117
|
+
out[off++] = (len >> 8) & 0xff;
|
|
118
|
+
const nlen = ~len & 0xffff;
|
|
119
|
+
out[off++] = nlen & 0xff;
|
|
120
|
+
out[off++] = (nlen >> 8) & 0xff;
|
|
121
|
+
out.set(data.subarray(pos, pos + len), off);
|
|
122
|
+
off += len;
|
|
123
|
+
pos += len;
|
|
124
|
+
}
|
|
125
|
+
writeUint32(out, off, adler32(data));
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
function writeUint32(buf, offset, value) {
|
|
129
|
+
buf[offset] = (value >>> 24) & 0xff;
|
|
130
|
+
buf[offset + 1] = (value >>> 16) & 0xff;
|
|
131
|
+
buf[offset + 2] = (value >>> 8) & 0xff;
|
|
132
|
+
buf[offset + 3] = value & 0xff;
|
|
133
|
+
}
|
|
134
|
+
// --- CRC32 (PNG polynomial 0xedb88320) ---
|
|
135
|
+
const CRC_TABLE = buildCrcTable();
|
|
136
|
+
function buildCrcTable() {
|
|
137
|
+
const table = new Uint32Array(256);
|
|
138
|
+
for (let n = 0; n < 256; n++) {
|
|
139
|
+
let c = n;
|
|
140
|
+
for (let k = 0; k < 8; k++) {
|
|
141
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
142
|
+
}
|
|
143
|
+
table[n] = c >>> 0;
|
|
144
|
+
}
|
|
145
|
+
return table;
|
|
146
|
+
}
|
|
147
|
+
function crc32(data) {
|
|
148
|
+
let crc = 0xffffffff;
|
|
149
|
+
for (let i = 0; i < data.length; i++) {
|
|
150
|
+
crc = CRC_TABLE[(crc ^ (data[i] ?? 0)) & 0xff] ^ (crc >>> 8);
|
|
151
|
+
}
|
|
152
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
153
|
+
}
|
|
154
|
+
// --- Adler32 (RFC 1950) ---
|
|
155
|
+
function adler32(data) {
|
|
156
|
+
const MOD = 65521;
|
|
157
|
+
let a = 1;
|
|
158
|
+
let b = 0;
|
|
159
|
+
for (let i = 0; i < data.length; i++) {
|
|
160
|
+
a = (a + (data[i] ?? 0)) % MOD;
|
|
161
|
+
b = (b + a) % MOD;
|
|
162
|
+
}
|
|
163
|
+
return ((b << 16) | a) >>> 0;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=png.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"png.js","sourceRoot":"","sources":["../../../src/engine/gfx/png.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type GfxSheet } from '../cart/index.js';
|
|
2
|
+
import { type RgbImage } from './png.js';
|
|
3
|
+
/**
|
|
4
|
+
* The `gfx render` JUDGE surface: turn a sprite (or the whole spritesheet) into a
|
|
5
|
+
* nearest-neighbour-upscaled, palette-accurate PNG a multimodal agent can LOOK at.
|
|
6
|
+
*
|
|
7
|
+
* The char grid (`gfx show`/`gfx set`) is the exact EDIT surface, but an LLM reads
|
|
8
|
+
* serialized hex poorly for gestalt/colour (the SVG parallel: models generate
|
|
9
|
+
* structured visual source well but read it back poorly). So this renders the
|
|
10
|
+
* ACTUAL pixels, upscaled enough to be viewable, with each colour index mapped to
|
|
11
|
+
* its exact PICO-8 palette RGB (single source of truth: {@link paletteColor}).
|
|
12
|
+
*
|
|
13
|
+
* Upscale is NEAREST-NEIGHBOUR (each source pixel becomes a `scale`x`scale` block
|
|
14
|
+
* of identical RGB) so the pixel-art stays crisp and the output bytes are
|
|
15
|
+
* deterministic. The scale is chosen so the longer edge lands near ~256px:
|
|
16
|
+
* - a single 8x8 sprite → scale 32 → 256x256 (see {@link SPRITE_RENDER_SCALE}).
|
|
17
|
+
* - the 128x128 sheet → scale 2 → 256x256 (see {@link SHEET_RENDER_SCALE}).
|
|
18
|
+
*/
|
|
19
|
+
/** Nearest-neighbour scale for a single 8x8 sprite: 8 * 32 = 256px. */
|
|
20
|
+
export declare const SPRITE_RENDER_SCALE = 32;
|
|
21
|
+
/** Nearest-neighbour scale for the 128x128 spritesheet: 128 * 2 = 256px. */
|
|
22
|
+
export declare const SHEET_RENDER_SCALE = 2;
|
|
23
|
+
/** The target viewable edge length both scales aim for (~256px). */
|
|
24
|
+
export declare const RENDER_TARGET_PX = 256;
|
|
25
|
+
/**
|
|
26
|
+
* Renders one sprite (0..255) to a PNG buffer: its 8x8 pixels, each mapped to its
|
|
27
|
+
* palette RGB, nearest-neighbour upscaled by {@link SPRITE_RENDER_SCALE}.
|
|
28
|
+
*/
|
|
29
|
+
export declare function renderSpritePng(sheet: GfxSheet, n: number): Uint8Array;
|
|
30
|
+
/**
|
|
31
|
+
* Renders the whole 128x128 spritesheet to a PNG buffer: every pixel mapped to
|
|
32
|
+
* its palette RGB, nearest-neighbour upscaled by {@link SHEET_RENDER_SCALE}.
|
|
33
|
+
*/
|
|
34
|
+
export declare function renderSheetPng(sheet: GfxSheet): Uint8Array;
|
|
35
|
+
/**
|
|
36
|
+
* Builds an upscaled {@link RgbImage} from a source of colour indices. Each
|
|
37
|
+
* source pixel (`getIndex(x, y)`) is mapped to its palette RGB and painted as a
|
|
38
|
+
* `scale`x`scale` block (nearest-neighbour), producing a `(srcW*scale)` x
|
|
39
|
+
* `(srcH*scale)` RGB buffer.
|
|
40
|
+
*/
|
|
41
|
+
export declare function upscale(srcWidth: number, srcHeight: number, getIndex: (x: number, y: number) => number, scale: number): RgbImage;
|
|
42
|
+
//# sourceMappingURL=render.d.ts.map
|