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.
Files changed (334) hide show
  1. package/LICENSE +661 -0
  2. package/dist/bin.d.ts +3 -0
  3. package/dist/bin.d.ts.map +1 -0
  4. package/dist/bin.js +4 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/cli.d.ts +15 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +59 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/commands/audio.d.ts +17 -0
  11. package/dist/commands/audio.d.ts.map +1 -0
  12. package/dist/commands/audio.js +315 -0
  13. package/dist/commands/audio.js.map +1 -0
  14. package/dist/commands/export.d.ts +36 -0
  15. package/dist/commands/export.d.ts.map +1 -0
  16. package/dist/commands/export.js +257 -0
  17. package/dist/commands/export.js.map +1 -0
  18. package/dist/commands/gfx.d.ts +28 -0
  19. package/dist/commands/gfx.d.ts.map +1 -0
  20. package/dist/commands/gfx.js +521 -0
  21. package/dist/commands/gfx.js.map +1 -0
  22. package/dist/commands/init.d.ts +34 -0
  23. package/dist/commands/init.d.ts.map +1 -0
  24. package/dist/commands/init.js +151 -0
  25. package/dist/commands/init.js.map +1 -0
  26. package/dist/commands/lint.d.ts +20 -0
  27. package/dist/commands/lint.d.ts.map +1 -0
  28. package/dist/commands/lint.js +90 -0
  29. package/dist/commands/lint.js.map +1 -0
  30. package/dist/commands/minify.d.ts +20 -0
  31. package/dist/commands/minify.d.ts.map +1 -0
  32. package/dist/commands/minify.js +129 -0
  33. package/dist/commands/minify.js.map +1 -0
  34. package/dist/commands/music.d.ts +13 -0
  35. package/dist/commands/music.d.ts.map +1 -0
  36. package/dist/commands/music.js +241 -0
  37. package/dist/commands/music.js.map +1 -0
  38. package/dist/commands/playtest.d.ts +29 -0
  39. package/dist/commands/playtest.d.ts.map +1 -0
  40. package/dist/commands/playtest.js +631 -0
  41. package/dist/commands/playtest.js.map +1 -0
  42. package/dist/commands/run.d.ts +30 -0
  43. package/dist/commands/run.d.ts.map +1 -0
  44. package/dist/commands/run.js +202 -0
  45. package/dist/commands/run.js.map +1 -0
  46. package/dist/commands/serve.d.ts +50 -0
  47. package/dist/commands/serve.d.ts.map +1 -0
  48. package/dist/commands/serve.js +213 -0
  49. package/dist/commands/serve.js.map +1 -0
  50. package/dist/commands/sfx.d.ts +13 -0
  51. package/dist/commands/sfx.d.ts.map +1 -0
  52. package/dist/commands/sfx.js +254 -0
  53. package/dist/commands/sfx.js.map +1 -0
  54. package/dist/commands/tokens.d.ts +36 -0
  55. package/dist/commands/tokens.d.ts.map +1 -0
  56. package/dist/commands/tokens.js +129 -0
  57. package/dist/commands/tokens.js.map +1 -0
  58. package/dist/commands/verify.d.ts +57 -0
  59. package/dist/commands/verify.d.ts.map +1 -0
  60. package/dist/commands/verify.js +213 -0
  61. package/dist/commands/verify.js.map +1 -0
  62. package/dist/commands/version.d.ts +12 -0
  63. package/dist/commands/version.d.ts.map +1 -0
  64. package/dist/commands/version.js +24 -0
  65. package/dist/commands/version.js.map +1 -0
  66. package/dist/engine/audio/index.d.ts +14 -0
  67. package/dist/engine/audio/index.d.ts.map +1 -0
  68. package/dist/engine/audio/index.js +14 -0
  69. package/dist/engine/audio/index.js.map +1 -0
  70. package/dist/engine/audio/music.d.ts +116 -0
  71. package/dist/engine/audio/music.d.ts.map +1 -0
  72. package/dist/engine/audio/music.js +138 -0
  73. package/dist/engine/audio/music.js.map +1 -0
  74. package/dist/engine/audio/section.d.ts +34 -0
  75. package/dist/engine/audio/section.d.ts.map +1 -0
  76. package/dist/engine/audio/section.js +67 -0
  77. package/dist/engine/audio/section.js.map +1 -0
  78. package/dist/engine/audio/sfx.d.ts +141 -0
  79. package/dist/engine/audio/sfx.d.ts.map +1 -0
  80. package/dist/engine/audio/sfx.js +506 -0
  81. package/dist/engine/audio/sfx.js.map +1 -0
  82. package/dist/engine/cart/cart.d.ts +75 -0
  83. package/dist/engine/cart/cart.d.ts.map +1 -0
  84. package/dist/engine/cart/cart.js +153 -0
  85. package/dist/engine/cart/cart.js.map +1 -0
  86. package/dist/engine/cart/errors.d.ts +29 -0
  87. package/dist/engine/cart/errors.d.ts.map +1 -0
  88. package/dist/engine/cart/errors.js +26 -0
  89. package/dist/engine/cart/errors.js.map +1 -0
  90. package/dist/engine/cart/gfx.d.ts +79 -0
  91. package/dist/engine/cart/gfx.d.ts.map +1 -0
  92. package/dist/engine/cart/gfx.js +171 -0
  93. package/dist/engine/cart/gfx.js.map +1 -0
  94. package/dist/engine/cart/index.d.ts +15 -0
  95. package/dist/engine/cart/index.d.ts.map +1 -0
  96. package/dist/engine/cart/index.js +15 -0
  97. package/dist/engine/cart/index.js.map +1 -0
  98. package/dist/engine/cart/map.d.ts +46 -0
  99. package/dist/engine/cart/map.d.ts.map +1 -0
  100. package/dist/engine/cart/map.js +118 -0
  101. package/dist/engine/cart/map.js.map +1 -0
  102. package/dist/engine/config/config.d.ts +74 -0
  103. package/dist/engine/config/config.d.ts.map +1 -0
  104. package/dist/engine/config/config.js +103 -0
  105. package/dist/engine/config/config.js.map +1 -0
  106. package/dist/engine/config/index.d.ts +7 -0
  107. package/dist/engine/config/index.d.ts.map +1 -0
  108. package/dist/engine/config/index.js +7 -0
  109. package/dist/engine/config/index.js.map +1 -0
  110. package/dist/engine/gfx/grid.d.ts +51 -0
  111. package/dist/engine/gfx/grid.d.ts.map +1 -0
  112. package/dist/engine/gfx/grid.js +116 -0
  113. package/dist/engine/gfx/grid.js.map +1 -0
  114. package/dist/engine/gfx/index.d.ts +17 -0
  115. package/dist/engine/gfx/index.d.ts.map +1 -0
  116. package/dist/engine/gfx/index.js +17 -0
  117. package/dist/engine/gfx/index.js.map +1 -0
  118. package/dist/engine/gfx/label.d.ts +34 -0
  119. package/dist/engine/gfx/label.d.ts.map +1 -0
  120. package/dist/engine/gfx/label.js +190 -0
  121. package/dist/engine/gfx/label.js.map +1 -0
  122. package/dist/engine/gfx/overlap.d.ts +42 -0
  123. package/dist/engine/gfx/overlap.d.ts.map +1 -0
  124. package/dist/engine/gfx/overlap.js +57 -0
  125. package/dist/engine/gfx/overlap.js.map +1 -0
  126. package/dist/engine/gfx/palette.d.ts +29 -0
  127. package/dist/engine/gfx/palette.d.ts.map +1 -0
  128. package/dist/engine/gfx/palette.js +46 -0
  129. package/dist/engine/gfx/palette.js.map +1 -0
  130. package/dist/engine/gfx/png.d.ts +31 -0
  131. package/dist/engine/gfx/png.d.ts.map +1 -0
  132. package/dist/engine/gfx/png.js +165 -0
  133. package/dist/engine/gfx/png.js.map +1 -0
  134. package/dist/engine/gfx/render.d.ts +42 -0
  135. package/dist/engine/gfx/render.d.ts.map +1 -0
  136. package/dist/engine/gfx/render.js +72 -0
  137. package/dist/engine/gfx/render.js.map +1 -0
  138. package/dist/engine/index.d.ts +22 -0
  139. package/dist/engine/index.d.ts.map +1 -0
  140. package/dist/engine/index.js +22 -0
  141. package/dist/engine/index.js.map +1 -0
  142. package/dist/engine/pico8/adapter.d.ts +267 -0
  143. package/dist/engine/pico8/adapter.d.ts.map +1 -0
  144. package/dist/engine/pico8/adapter.js +37 -0
  145. package/dist/engine/pico8/adapter.js.map +1 -0
  146. package/dist/engine/pico8/drive.d.ts +308 -0
  147. package/dist/engine/pico8/drive.d.ts.map +1 -0
  148. package/dist/engine/pico8/drive.js +581 -0
  149. package/dist/engine/pico8/drive.js.map +1 -0
  150. package/dist/engine/pico8/harness.d.ts +146 -0
  151. package/dist/engine/pico8/harness.d.ts.map +1 -0
  152. package/dist/engine/pico8/harness.js +251 -0
  153. package/dist/engine/pico8/harness.js.map +1 -0
  154. package/dist/engine/pico8/index.d.ts +21 -0
  155. package/dist/engine/pico8/index.d.ts.map +1 -0
  156. package/dist/engine/pico8/index.js +21 -0
  157. package/dist/engine/pico8/index.js.map +1 -0
  158. package/dist/engine/pico8/sentinel.d.ts +63 -0
  159. package/dist/engine/pico8/sentinel.d.ts.map +1 -0
  160. package/dist/engine/pico8/sentinel.js +86 -0
  161. package/dist/engine/pico8/sentinel.js.map +1 -0
  162. package/dist/engine/pico8/session-daemon-entry.d.ts +10 -0
  163. package/dist/engine/pico8/session-daemon-entry.d.ts.map +1 -0
  164. package/dist/engine/pico8/session-daemon-entry.js +11 -0
  165. package/dist/engine/pico8/session-daemon-entry.js.map +1 -0
  166. package/dist/engine/pico8/session-daemon-main.d.ts +44 -0
  167. package/dist/engine/pico8/session-daemon-main.d.ts.map +1 -0
  168. package/dist/engine/pico8/session-daemon-main.js +71 -0
  169. package/dist/engine/pico8/session-daemon-main.js.map +1 -0
  170. package/dist/engine/pico8/session-daemon.d.ts +68 -0
  171. package/dist/engine/pico8/session-daemon.d.ts.map +1 -0
  172. package/dist/engine/pico8/session-daemon.js +264 -0
  173. package/dist/engine/pico8/session-daemon.js.map +1 -0
  174. package/dist/engine/pico8/session.d.ts +140 -0
  175. package/dist/engine/pico8/session.d.ts.map +1 -0
  176. package/dist/engine/pico8/session.js +250 -0
  177. package/dist/engine/pico8/session.js.map +1 -0
  178. package/dist/engine/pico8/shell.d.ts +103 -0
  179. package/dist/engine/pico8/shell.d.ts.map +1 -0
  180. package/dist/engine/pico8/shell.js +324 -0
  181. package/dist/engine/pico8/shell.js.map +1 -0
  182. package/dist/engine/pico8/supervisor.d.ts +137 -0
  183. package/dist/engine/pico8/supervisor.d.ts.map +1 -0
  184. package/dist/engine/pico8/supervisor.js +140 -0
  185. package/dist/engine/pico8/supervisor.js.map +1 -0
  186. package/dist/engine/scaffold/index.d.ts +8 -0
  187. package/dist/engine/scaffold/index.d.ts.map +1 -0
  188. package/dist/engine/scaffold/index.js +8 -0
  189. package/dist/engine/scaffold/index.js.map +1 -0
  190. package/dist/engine/scaffold/scaffold.d.ts +49 -0
  191. package/dist/engine/scaffold/scaffold.d.ts.map +1 -0
  192. package/dist/engine/scaffold/scaffold.js +217 -0
  193. package/dist/engine/scaffold/scaffold.js.map +1 -0
  194. package/dist/engine/shrinko/adapter.d.ts +167 -0
  195. package/dist/engine/shrinko/adapter.d.ts.map +1 -0
  196. package/dist/engine/shrinko/adapter.js +107 -0
  197. package/dist/engine/shrinko/adapter.js.map +1 -0
  198. package/dist/engine/shrinko/index.d.ts +13 -0
  199. package/dist/engine/shrinko/index.d.ts.map +1 -0
  200. package/dist/engine/shrinko/index.js +13 -0
  201. package/dist/engine/shrinko/index.js.map +1 -0
  202. package/dist/engine/shrinko/shell.d.ts +69 -0
  203. package/dist/engine/shrinko/shell.d.ts.map +1 -0
  204. package/dist/engine/shrinko/shell.js +156 -0
  205. package/dist/engine/shrinko/shell.js.map +1 -0
  206. package/dist/engine/skills/index.d.ts +8 -0
  207. package/dist/engine/skills/index.d.ts.map +1 -0
  208. package/dist/engine/skills/index.js +8 -0
  209. package/dist/engine/skills/index.js.map +1 -0
  210. package/dist/engine/skills/skills.d.ts +104 -0
  211. package/dist/engine/skills/skills.d.ts.map +1 -0
  212. package/dist/engine/skills/skills.js +149 -0
  213. package/dist/engine/skills/skills.js.map +1 -0
  214. package/dist/index.d.ts +10 -0
  215. package/dist/index.d.ts.map +1 -0
  216. package/dist/index.js +10 -0
  217. package/dist/index.js.map +1 -0
  218. package/dist/version.d.ts +2 -0
  219. package/dist/version.d.ts.map +1 -0
  220. package/dist/version.js +19 -0
  221. package/dist/version.js.map +1 -0
  222. package/package.json +48 -2
  223. package/src/bin.ts +4 -0
  224. package/src/cli.ts +61 -0
  225. package/src/commands/audio.test.ts +280 -0
  226. package/src/commands/audio.ts +410 -0
  227. package/src/commands/export.test.ts +310 -0
  228. package/src/commands/export.ts +316 -0
  229. package/src/commands/gfx-io.test.ts +178 -0
  230. package/src/commands/gfx.test.ts +470 -0
  231. package/src/commands/gfx.ts +645 -0
  232. package/src/commands/init.test.ts +460 -0
  233. package/src/commands/init.ts +193 -0
  234. package/src/commands/lint.test.ts +137 -0
  235. package/src/commands/lint.ts +117 -0
  236. package/src/commands/minify.test.ts +166 -0
  237. package/src/commands/minify.ts +156 -0
  238. package/src/commands/music.test.ts +272 -0
  239. package/src/commands/music.ts +288 -0
  240. package/src/commands/playtest.test.ts +317 -0
  241. package/src/commands/playtest.ts +773 -0
  242. package/src/commands/run.test.ts +238 -0
  243. package/src/commands/run.ts +259 -0
  244. package/src/commands/serve.test.ts +218 -0
  245. package/src/commands/serve.ts +272 -0
  246. package/src/commands/sfx.test.ts +349 -0
  247. package/src/commands/sfx.ts +300 -0
  248. package/src/commands/tokens.test.ts +212 -0
  249. package/src/commands/tokens.ts +168 -0
  250. package/src/commands/verify.test.ts +238 -0
  251. package/src/commands/verify.ts +256 -0
  252. package/src/commands/version.test.ts +70 -0
  253. package/src/commands/version.ts +24 -0
  254. package/src/engine/audio/index.ts +47 -0
  255. package/src/engine/audio/music.test.ts +188 -0
  256. package/src/engine/audio/music.ts +218 -0
  257. package/src/engine/audio/section.test.ts +124 -0
  258. package/src/engine/audio/section.ts +79 -0
  259. package/src/engine/audio/sfx.test.ts +410 -0
  260. package/src/engine/audio/sfx.ts +694 -0
  261. package/src/engine/cart/cart.test.ts +304 -0
  262. package/src/engine/cart/cart.ts +211 -0
  263. package/src/engine/cart/errors.ts +39 -0
  264. package/src/engine/cart/fixtures/crlf.p8 +6 -0
  265. package/src/engine/cart/fixtures/full.p8 +174 -0
  266. package/src/engine/cart/fixtures/header-only.p8 +2 -0
  267. package/src/engine/cart/fixtures/malformed-no-header.p8 +2 -0
  268. package/src/engine/cart/fixtures/minimal.p8 +10 -0
  269. package/src/engine/cart/fixtures/no-trailing-newline.p8 +6 -0
  270. package/src/engine/cart/gfx.ts +195 -0
  271. package/src/engine/cart/index.ts +27 -0
  272. package/src/engine/cart/map.ts +134 -0
  273. package/src/engine/config/config.ts +115 -0
  274. package/src/engine/config/index.ts +12 -0
  275. package/src/engine/gfx/grid.test.ts +128 -0
  276. package/src/engine/gfx/grid.ts +136 -0
  277. package/src/engine/gfx/index.ts +34 -0
  278. package/src/engine/gfx/label.test.ts +105 -0
  279. package/src/engine/gfx/label.ts +229 -0
  280. package/src/engine/gfx/overlap.test.ts +71 -0
  281. package/src/engine/gfx/overlap.ts +66 -0
  282. package/src/engine/gfx/palette.test.ts +35 -0
  283. package/src/engine/gfx/palette.ts +54 -0
  284. package/src/engine/gfx/png.test.ts +111 -0
  285. package/src/engine/gfx/png.ts +196 -0
  286. package/src/engine/gfx/render.test.ts +180 -0
  287. package/src/engine/gfx/render.ts +106 -0
  288. package/src/engine/index.ts +21 -0
  289. package/src/engine/pico8/adapter.ts +303 -0
  290. package/src/engine/pico8/drive.test.ts +415 -0
  291. package/src/engine/pico8/drive.ts +729 -0
  292. package/src/engine/pico8/harness.test.ts +181 -0
  293. package/src/engine/pico8/harness.ts +354 -0
  294. package/src/engine/pico8/index.ts +20 -0
  295. package/src/engine/pico8/sentinel.test.ts +80 -0
  296. package/src/engine/pico8/sentinel.ts +93 -0
  297. package/src/engine/pico8/session-daemon-entry.ts +12 -0
  298. package/src/engine/pico8/session-daemon-main.ts +98 -0
  299. package/src/engine/pico8/session-daemon.test.ts +212 -0
  300. package/src/engine/pico8/session-daemon.ts +306 -0
  301. package/src/engine/pico8/session.test.ts +239 -0
  302. package/src/engine/pico8/session.ts +321 -0
  303. package/src/engine/pico8/shell.test.ts +583 -0
  304. package/src/engine/pico8/shell.ts +412 -0
  305. package/src/engine/pico8/supervisor.test.ts +105 -0
  306. package/src/engine/pico8/supervisor.ts +197 -0
  307. package/src/engine/scaffold/index.ts +15 -0
  308. package/src/engine/scaffold/scaffold.ts +236 -0
  309. package/src/engine/shrinko/adapter.ts +241 -0
  310. package/src/engine/shrinko/fixtures/count-no-compressed.txt +2 -0
  311. package/src/engine/shrinko/fixtures/count-over-budget.txt +3 -0
  312. package/src/engine/shrinko/fixtures/count-under-budget.txt +3 -0
  313. package/src/engine/shrinko/index.ts +33 -0
  314. package/src/engine/shrinko/shell.ts +224 -0
  315. package/src/engine/shrinko/shrinko.test.ts +182 -0
  316. package/src/engine/skills/index.ts +14 -0
  317. package/src/engine/skills/skills.test.ts +376 -0
  318. package/src/engine/skills/skills.ts +205 -0
  319. package/src/index.ts +13 -0
  320. package/src/skills/game-design-reference/SKILL.md +70 -0
  321. package/src/skills/game-jam/SKILL.md +45 -0
  322. package/src/skills/picopilot-art/SKILL.md +62 -0
  323. package/src/skills/picopilot-audio/SKILL.md +194 -0
  324. package/src/skills/picopilot-code/SKILL.md +97 -0
  325. package/src/skills/picopilot-code/reference/README.md +42 -0
  326. package/src/skills/picopilot-code/reference/mode7-racing.md +95 -0
  327. package/src/skills/picopilot-code/reference/platformer.md +119 -0
  328. package/src/skills/picopilot-code/reference/puzzle-grid.md +106 -0
  329. package/src/skills/picopilot-code/reference/rpg-menus-dialog.md +122 -0
  330. package/src/skills/picopilot-code/reference/top-down-adventure.md +117 -0
  331. package/src/skills/picopilot-code/reference/twin-stick-arcade.md +128 -0
  332. package/src/skills/picopilot-debug/SKILL.md +195 -0
  333. package/src/skills/picopilot-overview/SKILL.md +52 -0
  334. package/src/version.ts +22 -0
@@ -0,0 +1,581 @@
1
+ /**
2
+ * The `playtest` DRIVE-transform (ADR-0011): the PURE, CI-testable core that
3
+ * turns an ARBITRARY cart's Lua + a driven-run SPEC into a THROWAWAY driven cart
4
+ * whose input and frame loop the harness OWNS. This is the load-bearing seam of
5
+ * `picopilot playtest`, kept pure (cart text + spec in -> cart text + encoded
6
+ * command blocks out) so its correctness (the `btn`/`btnp` shim, the
7
+ * `_update`/`_draw` wrap, btnp-edge reconstruction, the opcode/block codec, the
8
+ * opt-in `srand`) is unit-tested WITHOUT the paid PICO-8 binary. Only the live
9
+ * drive-and-capture needs a real session (the manual/opt-in tier, ADR-0011).
10
+ *
11
+ * The recipe is DECIDED + spike-verified (PICO-8 v0.2.7). This module IMPLEMENTS
12
+ * it; it is not open design. The three load-bearing verified facts:
13
+ * 1. Redefine the global `btn`/`btnp` to read a per-frame held-buttons byte
14
+ * from `serial(0x804)`, RECONSTRUCTING `btnp` EDGES from the held level (a
15
+ * single scripted press = exactly one `btnp` true frame, not held-repeat).
16
+ * 2. The host<->cart transport is FIXED-SIZE command blocks (small unpadded
17
+ * writes to a live `-x` stdin coalesce/drop in the OS pipe buffer), drained
18
+ * ONE block per frame gated on the step-budget, with a `printh` ACK per
19
+ * completed command (the resumable session waits on the ACK; the one-shot
20
+ * ignores it).
21
+ * 3. The harness OWNS the frame loop: it wraps `_update`/`_update60`/`_draw` so
22
+ * a STEP advances exactly N frames and a PAUSE (budget 0) FREEZES the game
23
+ * (its callbacks skipped -> all state held, incl. logic in `_draw`).
24
+ *
25
+ * The one-shot command sends the whole script up front and exits; the cart-side
26
+ * machine here is identical to what the resumable session reuses (ADR-0011).
27
+ */
28
+ import { Cart } from '../cart/index.js';
29
+ import { CartParseError } from '../cart/errors.js';
30
+ import { DONE_SENTINEL } from './adapter.js';
31
+ /** A structured drive-harness-build failure. */
32
+ export class DriveError extends Error {
33
+ code;
34
+ constructor(code, message) {
35
+ super(message);
36
+ this.name = 'DriveError';
37
+ this.code = code;
38
+ }
39
+ }
40
+ /**
41
+ * The PICO-8 button bit order (the `btn(i)`/`btnp(i)` index), used by the
42
+ * held-buttons byte the harness pipes over `serial(0x804)`: bit `i` set = button
43
+ * `i` held this frame. Matches PICO-8's own `btn` indices (verified).
44
+ */
45
+ export const BUTTON_BITS = {
46
+ left: 0,
47
+ right: 1,
48
+ up: 2,
49
+ down: 3,
50
+ o: 4,
51
+ x: 5,
52
+ };
53
+ /** The valid button-bit range [0,5] a held-buttons byte can set. */
54
+ export const BUTTON_BIT_MIN = 0;
55
+ export const BUTTON_BIT_MAX = 5;
56
+ /** The accepted button NAMES (and their bits) the live `input` verb parses. */
57
+ export const BUTTON_NAMES = {
58
+ left: BUTTON_BITS.left,
59
+ right: BUTTON_BITS.right,
60
+ up: BUTTON_BITS.up,
61
+ down: BUTTON_BITS.down,
62
+ o: BUTTON_BITS.o,
63
+ x: BUTTON_BITS.x,
64
+ // Common single-letter directional aliases.
65
+ l: BUTTON_BITS.left,
66
+ r: BUTTON_BITS.right,
67
+ u: BUTTON_BITS.up,
68
+ d: BUTTON_BITS.down,
69
+ // PICO-8 KEY convention: z = the O button, x = the X button. Agents type
70
+ // these (they think in keys), so accept them. (`x` already = the X button.)
71
+ z: BUTTON_BITS.o,
72
+ };
73
+ /**
74
+ * Parses a live-session `input` button spec into a single HELD-buttons byte: a
75
+ * comma/space-separated list of button NAMES (`left right up down o x`, plus the
76
+ * single-letter aliases `l r u d`), e.g. `"right o"` = hold Right + O this turn.
77
+ * Case-insensitive; an empty spec is the byte 0 (release all). An unknown name is
78
+ * a structured {@link DriveError} (`playtest-input-invalid`), never a silent skip.
79
+ * This is the SESSION-facing input surface (per-turn held level); the one-shot's
80
+ * {@link parseInputScript} is the frame-scripted surface. Both land on the same
81
+ * held byte -> {@link btnpEdges} machine.
82
+ */
83
+ export function parseButtons(spec) {
84
+ let held = 0;
85
+ for (const raw of spec.split(/[\s,]+/)) {
86
+ if (raw.trim().length === 0)
87
+ continue;
88
+ // Shares the button vocabulary with `--input` (names or bit digits 0..5).
89
+ held |= 1 << parseButtonToken(raw);
90
+ }
91
+ return held;
92
+ }
93
+ /**
94
+ * The FIXED command-block size in bytes (ADR-0011). Load-bearing: small unpadded
95
+ * writes to a live `pico8 -x` stdin coalesce/drop in the OS pipe buffer, so every
96
+ * command is sent as a block of exactly this many bytes (opcode + arg + pad). The
97
+ * cart reads exactly this many bytes per frame and buffers a partial block across
98
+ * frames. 4 bytes comfortably clears the 1-2 byte coalesce window the spike hit.
99
+ */
100
+ export const BLOCK_SIZE = 4;
101
+ /**
102
+ * The tagged opcodes carried in a command block's first byte (ADR-0011). One
103
+ * command per block; the cart drains one block per frame against the step budget.
104
+ * - STEP<n>: advance exactly `n` frames (the arg byte), then pause.
105
+ * - INPUT<b>: set the held-buttons byte to `b` (the arg byte) for the next STEP.
106
+ * - SHOT: screenshot the current (frozen) frame.
107
+ * - PAUSE: set the step budget to 0 (freeze; callbacks skipped).
108
+ * - QUIT: print the done-sentinel so the launcher kills PICO-8.
109
+ */
110
+ export const OPCODE = {
111
+ step: 1,
112
+ input: 2,
113
+ shot: 3,
114
+ pause: 4,
115
+ quit: 5,
116
+ };
117
+ /**
118
+ * The cart-side ACK lines the drive shim `printh`s when a command COMPLETES
119
+ * (ADR-0011: the resumable session WAITS for the ACK before sending the next
120
+ * command; the one-shot ignores them and waits only for the QUIT sentinel). One
121
+ * source of truth shared by the Lua shim (which prints them) and the host-side
122
+ * {@link import('./session.js').DriveSession} (which waits on them), so the
123
+ * handshake can never drift between the two ends.
124
+ *
125
+ * The load-bearing one is {@link ACK.stepDone}: a STEP's ACK fires when the frame
126
+ * budget REACHES ZERO (the N frames have actually advanced), NOT when the STEP
127
+ * block is merely read. That is what makes stepping deterministic, the host acts
128
+ * on a SETTLED, known frame, never mid-step. INPUT/SHOT/PAUSE complete the frame
129
+ * they are read (instantaneous), so they ACK at read time.
130
+ */
131
+ export const ACK = {
132
+ /** A STEP's frame budget has fully drained (the N frames advanced). */
133
+ stepDone: '__PP_ACK_STEP_DONE__',
134
+ /** The held-buttons byte was set (INPUT). */
135
+ input: '__PP_ACK_INPUT__',
136
+ /** A screenshot was queued for the current frame (SHOT). */
137
+ shot: '__PP_ACK_SHOT__',
138
+ /** The frame budget was forced to 0 (PAUSE). */
139
+ pause: '__PP_ACK_PAUSE__',
140
+ };
141
+ /**
142
+ * Encodes one {@link DriveCommand} into a FIXED-SIZE ({@link BLOCK_SIZE}) block:
143
+ * `[opcode, arg, 0, 0]`. `arg` carries the STEP frame count or the INPUT held
144
+ * byte; SHOT/PAUSE/QUIT ignore it (0). Fixed size is the point (ADR-0011): the
145
+ * cart always consumes exactly {@link BLOCK_SIZE} bytes per read.
146
+ */
147
+ export function encodeCommand(cmd) {
148
+ const block = new Uint8Array(BLOCK_SIZE); // zero-padded by construction
149
+ switch (cmd.op) {
150
+ case 'step':
151
+ block[0] = OPCODE.step;
152
+ block[1] = cmd.frames & 0xff;
153
+ break;
154
+ case 'input':
155
+ block[0] = OPCODE.input;
156
+ block[1] = cmd.held & 0xff;
157
+ break;
158
+ case 'shot':
159
+ block[0] = OPCODE.shot;
160
+ break;
161
+ case 'pause':
162
+ block[0] = OPCODE.pause;
163
+ break;
164
+ case 'quit':
165
+ block[0] = OPCODE.quit;
166
+ break;
167
+ }
168
+ return block;
169
+ }
170
+ /** Encodes a whole command script into a single concatenated block stream. */
171
+ export function encodeScript(script) {
172
+ const out = new Uint8Array(script.length * BLOCK_SIZE);
173
+ script.forEach((cmd, i) => out.set(encodeCommand(cmd), i * BLOCK_SIZE));
174
+ return out;
175
+ }
176
+ /**
177
+ * A stateful block DECODER mirroring the cart-side shim: feed it raw byte chunks
178
+ * (a live `-x` stdin delivers arbitrary-sized reads), it buffers partial blocks
179
+ * across chunks and yields exactly one {@link DriveCommand} per completed
180
+ * {@link BLOCK_SIZE}-byte block. This is the TS twin of the Lua decoder, so the
181
+ * codec (including "a block split across two reads still decodes once") is
182
+ * asserted in CI without the binary.
183
+ */
184
+ export class BlockDecoder {
185
+ buffer = [];
186
+ /** Feeds a chunk of bytes; returns the commands newly completed by it. */
187
+ push(chunk) {
188
+ for (const b of chunk)
189
+ this.buffer.push(b & 0xff);
190
+ const out = [];
191
+ while (this.buffer.length >= BLOCK_SIZE) {
192
+ const block = this.buffer.splice(0, BLOCK_SIZE);
193
+ out.push(decodeBlock(block));
194
+ }
195
+ return out;
196
+ }
197
+ }
198
+ /** Decodes one {@link BLOCK_SIZE}-byte block into a {@link DriveCommand}. */
199
+ export function decodeBlock(block) {
200
+ const [op = 0, arg = 0] = block;
201
+ switch (op) {
202
+ case OPCODE.step:
203
+ return { op: 'step', frames: arg };
204
+ case OPCODE.input:
205
+ return { op: 'input', held: arg };
206
+ case OPCODE.shot:
207
+ return { op: 'shot' };
208
+ case OPCODE.pause:
209
+ return { op: 'pause' };
210
+ case OPCODE.quit:
211
+ return { op: 'quit' };
212
+ default:
213
+ // An unknown opcode is a no-op step-0 (never a crash); the codec is total.
214
+ return { op: 'step', frames: 0 };
215
+ }
216
+ }
217
+ /**
218
+ * Reconstructs `btnp` EDGES from a per-frame HELD-buttons sequence (ADR-0011,
219
+ * the load-bearing verified fact). Given the held byte for each frame, returns
220
+ * the `btnp` byte for each frame: bit `i` is set only on the frame the button
221
+ * TRANSITIONS 0 -> 1 (a rising edge), so a single scripted press triggers exactly
222
+ * one `btnp` (menus/start/single-actions behave), a hold triggers one edge then
223
+ * stays held (no repeat), and a release+press triggers two edges. This is a pure
224
+ * mirror of the cart-side shim (`btnp = held & ~prev`), tested independently.
225
+ */
226
+ export function btnpEdges(held) {
227
+ let prev = 0;
228
+ return held.map((cur) => {
229
+ const edge = cur & ~prev & 0xff;
230
+ prev = cur;
231
+ return edge;
232
+ });
233
+ }
234
+ /**
235
+ * Expands scripted presses into a per-frame HELD-buttons byte array of length
236
+ * `frames`: frame `f`'s byte has bit `p.bit` set for every press `p` whose
237
+ * `[from,to]` window covers `f`. This is the level signal the harness pipes; the
238
+ * cart reconstructs `btnp` edges from it (see {@link btnpEdges}).
239
+ */
240
+ export function pressesToHeld(presses, frames) {
241
+ const held = new Array(frames).fill(0);
242
+ for (const p of presses) {
243
+ for (let f = Math.max(0, p.from); f <= p.to && f < frames; f++) {
244
+ held[f] = (held[f] ?? 0) | (1 << p.bit);
245
+ }
246
+ }
247
+ return held;
248
+ }
249
+ /**
250
+ * Resolves a single BUTTON token to its bit. Accepts a NAME (`left right up down
251
+ * o x`, plus aliases `l r u d`, case-insensitive) OR a bare bit digit `0..5`, so
252
+ * BOTH `playtest` input surfaces share one button vocabulary (the `--input`
253
+ * timeline and the session `input` verb). An unknown token is a structured
254
+ * {@link DriveError}.
255
+ */
256
+ export function parseButtonToken(token) {
257
+ const t = token.trim().toLowerCase();
258
+ const named = BUTTON_NAMES[t];
259
+ if (named !== undefined)
260
+ return named;
261
+ if (/^\d+$/.test(t)) {
262
+ const bit = Number(t);
263
+ if (bit >= BUTTON_BIT_MIN && bit <= BUTTON_BIT_MAX)
264
+ return bit;
265
+ }
266
+ throw new DriveError('playtest-input-invalid', `unknown button "${token}": use a name (left/right/up/down/o/x, or l/r/u/d) or a bit 0..5 (0=L 1=R 2=U 3=D 4=O 5=X)`);
267
+ }
268
+ /**
269
+ * Parses an `--input` spec into scripted presses. Each comma-separated token is
270
+ * either a TIMELINE entry `frame:button` / `from-to:button` (a press at that
271
+ * frame, or a hold across the inclusive window) OR a BARE button (a name/key with
272
+ * no `frame:`), which is a press at frame 0, so a name-only spec like `"z"` or
273
+ * `"o right"` (the natural "just press this" form, matching the session `input`
274
+ * verb) Just Works. `button` is a NAME (`o x left right up down`, aliases
275
+ * `l r u d`, PICO-8 keys `z`/`x`) or a bit digit `0..5`, so both `playtest` input
276
+ * surfaces share ONE vocabulary. E.g. `"3:o, 18-22:o, 20:right"` = press O at
277
+ * frame 3, hold O frames 18..22, press right at frame 20; `"z"` = press O at
278
+ * frame 0. Whitespace around tokens is ignored (a bare list may be space- or
279
+ * comma-separated). An unknown button is a structured {@link DriveError}.
280
+ */
281
+ export function parseInputScript(spec) {
282
+ const presses = [];
283
+ // A bare button list (no `frame:` colon anywhere) is the "just press these"
284
+ // form: each is a press at frame 0. Allow space- OR comma-separation for it.
285
+ const hasTimeline = spec.includes(':');
286
+ const tokens = hasTimeline ? spec.split(',') : spec.split(/[\s,]+/);
287
+ for (const raw of tokens) {
288
+ const tok = raw.trim();
289
+ if (tok.length === 0)
290
+ continue;
291
+ const m = /^(\d+)(?:-(\d+))?:(.+)$/.exec(tok);
292
+ if (m === null) {
293
+ if (tok.includes(':')) {
294
+ throw new DriveError('playtest-input-invalid', `bad input token "${tok}": expected "frame:button" or "from-to:button" (button = a name like o/right or a bit 0..5)`);
295
+ }
296
+ // A bare button = a press at frame 0.
297
+ presses.push({ from: 0, to: 0, bit: parseButtonToken(tok) });
298
+ continue;
299
+ }
300
+ const from = Number(m[1]);
301
+ const to = m[2] !== undefined ? Number(m[2]) : from;
302
+ const bit = parseButtonToken(m[3]);
303
+ if (to < from) {
304
+ throw new DriveError('playtest-input-invalid', `hold window end ${to} precedes start ${from} in "${tok}"`);
305
+ }
306
+ presses.push({ from, to, bit });
307
+ }
308
+ return presses;
309
+ }
310
+ /**
311
+ * The GENERIC input default (ADR-0011, US #9): applied when `--input` is omitted.
312
+ * Tuned to REACH play and stay there briefly for common one-button / runner /
313
+ * flappy shapes: press O once to START, a few well-spaced single-frame O presses
314
+ * (clean `btnp` edges) so a one-button game acts without dying instantly, a short
315
+ * O HOLD for hold-to-thrust games, and a right-nudge for directional runners. It
316
+ * is a best-effort default, NOT a universal driver; an unusual control scheme
317
+ * should pass an explicit per-cart `--input` (documented in the command help).
318
+ */
319
+ export const GENERIC_INPUT = [
320
+ { from: 3, to: 3, bit: BUTTON_BITS.o }, // press O to start
321
+ { from: 16, to: 16, bit: BUTTON_BITS.o }, // a gentle action
322
+ { from: 18, to: 22, bit: BUTTON_BITS.o }, // a short hold (thrust games)
323
+ { from: 20, to: 20, bit: BUTTON_BITS.right }, // a right-nudge (runners)
324
+ { from: 24, to: 24, bit: BUTTON_BITS.o },
325
+ { from: 30, to: 30, bit: BUTTON_BITS.o },
326
+ ];
327
+ /**
328
+ * The default number of frames the generic/one-shot driver advances the cart, and
329
+ * the shot points within it. Chosen (per the spike) to catch LIVE play soon after
330
+ * the start press (before any death/retry churn) then a couple more spread out.
331
+ */
332
+ export const DEFAULT_DRIVE_FRAMES = 40;
333
+ export const DEFAULT_SHOT_FRAMES = [12, 22, 34];
334
+ /**
335
+ * Inlines `#include <file>` directives in a cart's Lua so the driven cart is
336
+ * self-contained (it runs from a throwaway temp dir where the included files are
337
+ * absent, and `definedCallbacks` must SEE the real `_update`/`_draw` that live in
338
+ * the included file). Matches PICO-8's own `#include`: a line whose first
339
+ * non-space token is `#include`, the rest of the line is the path. Resolves
340
+ * recursively (an included file may itself `#include`), guarding against cycles.
341
+ * A path the resolver cannot read is left verbatim (best-effort, like the rest of
342
+ * the transform). With no resolver, the Lua is returned unchanged.
343
+ */
344
+ export function resolveIncludes(lua, readInclude, seen = new Set()) {
345
+ if (readInclude === undefined)
346
+ return lua;
347
+ return lua
348
+ .split('\n')
349
+ .map((line) => {
350
+ const m = /^\s*#include\s+(.+?)\s*$/.exec(line);
351
+ if (m === null)
352
+ return line;
353
+ const path = m[1];
354
+ if (seen.has(path))
355
+ return line; // cycle guard: leave it
356
+ const content = readInclude(path);
357
+ if (content === undefined)
358
+ return line; // unreadable: leave verbatim
359
+ return resolveIncludes(content, readInclude, new Set([...seen, path]));
360
+ })
361
+ .join('\n');
362
+ }
363
+ /** The default base filename SHOT names each screenshot (`play0.png`, ...). */
364
+ export const SHOT_BASENAME = 'play';
365
+ /**
366
+ * Builds the ONE-SHOT command SCRIPT from a spec: the full sequence of INPUT /
367
+ * STEP / SHOT / QUIT blocks that drives the cart frame-by-frame. Each frame is an
368
+ * INPUT (the held byte for that frame) followed by a single-frame STEP; SHOT
369
+ * blocks are interleaved at the shot frames; a trailing QUIT ends the run. The
370
+ * cart drains one block per frame, so the whole script is sent up front and the
371
+ * cart replays it deterministically (ADR-0011: the one-shot sends everything and
372
+ * exits; the block+ACK transport lets it queue without loss).
373
+ */
374
+ export function buildDriveScript(spec) {
375
+ const presses = spec.presses ?? GENERIC_INPUT;
376
+ const frames = spec.frames ?? DEFAULT_DRIVE_FRAMES;
377
+ const shotFrames = new Set(spec.shotFrames ?? DEFAULT_SHOT_FRAMES);
378
+ const held = pressesToHeld(presses, frames);
379
+ const script = [];
380
+ let prevHeld = -1;
381
+ for (let f = 0; f < frames; f++) {
382
+ const h = held[f] ?? 0;
383
+ // Only re-send INPUT when the held level CHANGES (the cart holds the last
384
+ // value); this keeps the block stream compact without losing any edge.
385
+ if (h !== prevHeld) {
386
+ script.push({ op: 'input', held: h });
387
+ prevHeld = h;
388
+ }
389
+ script.push({ op: 'step', frames: 1 });
390
+ if (shotFrames.has(f))
391
+ script.push({ op: 'shot' });
392
+ }
393
+ script.push({ op: 'quit' });
394
+ return script;
395
+ }
396
+ /**
397
+ * Which frame callbacks the cart defines. `_update60` (60fps) takes precedence
398
+ * over `_update` (30fps) in PICO-8; a cart may define only `_draw` (a draw-only
399
+ * cart). The harness wraps whichever exist and is no-op-safe when one is missing.
400
+ */
401
+ function definedCallbacks(lua) {
402
+ return {
403
+ update: /function\s+_update\s*\(/.test(lua),
404
+ update60: /function\s+_update60\s*\(/.test(lua),
405
+ draw: /function\s+_draw\s*\(/.test(lua),
406
+ };
407
+ }
408
+ /**
409
+ * The DRIVE shim, prepended to a throwaway copy of the cart (ADR-0011). It:
410
+ * - redefines the global `btn`/`btnp` to read a per-frame held-buttons byte from
411
+ * a harness-owned variable, reconstructing `btnp` edges (`held & ~prev`);
412
+ * - decodes FIXED-SIZE ({@link BLOCK_SIZE}) command blocks from `serial(0x804)`,
413
+ * buffering partial blocks across frames, one block drained per frame;
414
+ * - owns the frame loop: a STEP advances exactly N frames (the budget); budget 0
415
+ * FREEZES the game (both `_update*` AND `_draw` skipped between shots, so
416
+ * logic-in-draw also freezes -> a stable framebuffer). A SHOT renders the
417
+ * current state once (via `_draw`) THEN screenshots it, so the capture always
418
+ * reflects the frame just drawn (never a stale/pre-draw framebuffer);
419
+ * - `printh`s an ACK when a command COMPLETES (INPUT/SHOT/PAUSE at read time; a
420
+ * STEP when its frame budget drains to 0, so a step ACK means the N frames
421
+ * actually ran, see {@link ACK}), which the resumable session waits on; the
422
+ * one-shot ignores the ACKs and waits only for the done-sentinel on QUIT.
423
+ *
424
+ * The shim wraps whichever of `_update`/`_update60`/`_draw` the cart defines by
425
+ * capturing them AFTER the cart's code runs (in `_init`-time via a deferred
426
+ * hook), so it is no-op-safe when a callback is missing.
427
+ */
428
+ function driveShim(callbacks, sentinel, shotBasename) {
429
+ // PICO-8 sets _update60 XOR _update; capture whichever exists as the ticked
430
+ // callback. _draw is separate. We freeze BOTH when the budget is 0.
431
+ return [
432
+ '-- picopilot playtest drive shim (auto-injected; throwaway; ADR-0011).',
433
+ '-- Redefines btn/btnp to a harness-piped held-buttons byte (serial 0x804),',
434
+ '-- reconstructs btnp edges, and owns the frame loop (step/pause/shot/quit)',
435
+ '-- over fixed-size command blocks. The entry cart is UNTOUCHED (copy).',
436
+ '__drv_held=0 __drv_prev=0',
437
+ '__drv_budget=0 __drv_buf="" __drv_shot=0 __drv_pshot=nil',
438
+ `__drv_bs=${BLOCK_SIZE}`,
439
+ `function btn(i,p) if i==nil then return __drv_held end return (__drv_held & (1<<i))!=0 end`,
440
+ 'function btnp(i,p) return (__drv_held & (1<<i))!=0 and (__drv_prev & (1<<i))==0 end',
441
+ '-- Read exactly one fixed-size block per frame from stdin; buffer partials.',
442
+ 'function __drv_read_block()',
443
+ ' local n=serial(0x804,0x4300,__drv_bs)',
444
+ ' for j=0,n-1 do __drv_buf=__drv_buf..chr(peek(0x4300+j)) end',
445
+ ' if #__drv_buf<__drv_bs then return nil end',
446
+ ' local op=ord(__drv_buf,1) local arg=ord(__drv_buf,2)',
447
+ ' __drv_buf=sub(__drv_buf,__drv_bs+1)',
448
+ ' return op,arg',
449
+ 'end',
450
+ '-- Drain one command block per frame, gated on the step budget.',
451
+ 'function __drv_poll()',
452
+ ' if __drv_budget>0 then return end',
453
+ ' local op,arg=__drv_read_block()',
454
+ ' if op==nil then return end',
455
+ ` if op==${OPCODE.input} then __drv_held=arg printh("${ACK.input}")`,
456
+ // STEP does NOT ACK at read time; its ACK (`__drv_step_done`) fires when the
457
+ // budget drains to 0 (below), so the host learns the N frames ACTUALLY ran.
458
+ ` elseif op==${OPCODE.step} then __drv_budget=arg`,
459
+ ` elseif op==${OPCODE.shot} then __drv_pshot="${shotBasename}"..__drv_shot __drv_shot+=1 printh("${ACK.shot}")`,
460
+ ` elseif op==${OPCODE.pause} then __drv_budget=0 printh("${ACK.pause}")`,
461
+ ` elseif op==${OPCODE.quit} then printh("${sentinel}")`,
462
+ ' end',
463
+ 'end',
464
+ '-- Frame ownership: advance the ticked callback only while budget>0, and',
465
+ '-- freeze _draw too so logic-in-draw holds. __drv_tick is set below.',
466
+ '-- btnp edges are computed relative to the PREVIOUS TICK (not the previous',
467
+ '-- host frame): INPUT and STEP arrive on separate frames, so __drv_prev is',
468
+ '-- updated to the held level only AFTER a tick runs. A single scripted press',
469
+ '-- (INPUT held then INPUT 0, each followed by a STEP) is thus one btnp edge.',
470
+ 'function __drv_step()',
471
+ ' __drv_poll()',
472
+ ' if __drv_budget>0 then',
473
+ ' __drv_budget-=1',
474
+ ' if __drv_tick then __drv_tick() end',
475
+ ' __drv_prev=__drv_held',
476
+ // A STEP completes (its ACK) the frame its budget reaches 0: the requested N
477
+ // frames have now advanced, so the host acts on a settled frame (ADR-0011).
478
+ ` if __drv_budget==0 then printh("${ACK.stepDone}") end`,
479
+ ' return true',
480
+ ' end',
481
+ ' return false',
482
+ 'end',
483
+ '',
484
+ ].join('\n');
485
+ }
486
+ /**
487
+ * The wrapper tab, appended AFTER the cart's code so the cart's callbacks are
488
+ * already defined when we capture them. It saves the cart's own
489
+ * `_update60`/`_update`/`_draw`, then installs harness-owned ones: the ticked
490
+ * callback runs the drain/step (advancing the cart's real update only while the
491
+ * budget allows), and `_draw` runs the cart's draw when a step advanced this
492
+ * frame OR a SHOT is pending (so a paused game shows a STABLE frame yet a SHOT
493
+ * always captures a freshly-drawn frame). A pending SHOT screenshots AFTER the
494
+ * cart's draw, so the capture reflects the frame just rendered, never a stale
495
+ * pre-draw framebuffer. No-op-safe when a callback is missing (draw-only carts).
496
+ */
497
+ function wrapperTab(callbacks) {
498
+ const ticked = callbacks.update60
499
+ ? '_update60'
500
+ : callbacks.update
501
+ ? '_update'
502
+ : undefined;
503
+ const lines = [
504
+ '',
505
+ '-->8',
506
+ '-- picopilot playtest wrapper (auto-injected; throwaway; ADR-0011).',
507
+ '-- Captures the cart callbacks AFTER its code ran, then OWNS the frame loop.',
508
+ ];
509
+ // __drv_tick is the cart's real ticked callback (nil for a draw-only cart);
510
+ // __drv_step advances it under the budget.
511
+ if (ticked !== undefined) {
512
+ lines.push(`__drv_tick=${ticked}`);
513
+ }
514
+ lines.push('__drv_draw=_draw', '__drv_advanced=false');
515
+ // The harness-owned ticked callback: always defined so the drain runs even for
516
+ // a draw-only cart. It advances the cart's real callback via __drv_step.
517
+ lines.push('function _update() __drv_advanced=__drv_step() end',
518
+ // A cart that defined _update60 gets a 60fps loop; alias it.
519
+ callbacks.update60
520
+ ? 'function _update60() __drv_advanced=__drv_step() end'
521
+ : '',
522
+ // _draw renders the cart's draw when a step advanced (live) OR a SHOT is
523
+ // pending (render the frozen state for the capture). Between shots while
524
+ // paused it is skipped, so logic-in-draw freezes and the last frame persists.
525
+ // A pending SHOT screenshots AFTER the draw, so the PNG is the frame just
526
+ // drawn, never a stale pre-draw framebuffer.
527
+ 'function _draw()', ' if __drv_draw and (__drv_advanced or __drv_pshot!=nil) then __drv_draw() end', ' if __drv_pshot!=nil then', ' extcmd("set_filename",__drv_pshot) extcmd("screen") __drv_pshot=nil', ' end', 'end', '');
528
+ return lines.filter((l) => l !== '').join('\n') + '\n';
529
+ }
530
+ /**
531
+ * Builds the throwaway DRIVE harness (ADR-0011). Takes the user's cart text + a
532
+ * {@link DriveSpec} and produces a new cart with the SAME `__gfx__`/`__sfx__`/...
533
+ * and a `__lua__` that is: the drive shim, then the cart's own Lua (optionally
534
+ * prefixed with `srand(seed)` iff `--seed` given), then the wrapper tab. The
535
+ * entry's own cart bytes are NEVER mutated (we transform a parsed COPY).
536
+ *
537
+ * Returns the driven cart text plus the encoded command blocks the command pipes
538
+ * to stdin. Kept pure so every path is unit-tested without the paid binary.
539
+ *
540
+ * @throws {DriveError} an unparseable cart.
541
+ */
542
+ export function buildDriveHarness(cartText, spec = {}) {
543
+ let cart;
544
+ try {
545
+ cart = Cart.parse(cartText);
546
+ }
547
+ catch (e) {
548
+ if (e instanceof CartParseError) {
549
+ throw new DriveError('playtest-cart-parse-error', `cart does not parse: ${e.message}`);
550
+ }
551
+ throw e;
552
+ }
553
+ const sentinel = spec.sentinel ?? DONE_SENTINEL;
554
+ const shotBasename = spec.shotBasename ?? SHOT_BASENAME;
555
+ // Inline #include directives so the driven cart is self-contained in its temp
556
+ // dir AND so definedCallbacks sees the real _update/_draw (which live in the
557
+ // included file for picopilot's standard `#include main.lua` scaffold).
558
+ const cartLua = resolveIncludes(cart.getSection('lua') ?? '', spec.readInclude);
559
+ const callbacks = definedCallbacks(cartLua);
560
+ // The opt-in srand: injected at the TOP of the cart's own code iff a seed is
561
+ // given, never silently (it changes game behaviour). It runs before the cart's
562
+ // _init/first frame, so rnd() is deterministic from the start.
563
+ const seedLine = spec.seed !== undefined ? `srand(${spec.seed})\n` : '';
564
+ const lua = [
565
+ driveShim(callbacks, sentinel, shotBasename),
566
+ seedLine + cartLua,
567
+ wrapperTab(callbacks),
568
+ ].join('\n');
569
+ cart.setSection('lua', lua);
570
+ const script = buildDriveScript(spec);
571
+ const blocks = encodeScript(script);
572
+ const shotCount = script.filter((c) => c.op === 'shot').length;
573
+ return {
574
+ cartText: cart.serialize(),
575
+ script,
576
+ blocks,
577
+ shotBasename,
578
+ shotCount,
579
+ };
580
+ }
581
+ //# sourceMappingURL=drive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"drive.js","sourceRoot":"","sources":["../../../src/engine/pico8/drive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAC,IAAI,EAAC,MAAM,kBAAkB,CAAC;AACtC,OAAO,EAAC,cAAc,EAAC,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAC,aAAa,EAAC,MAAM,cAAc,CAAC;AAY3C,gDAAgD;AAChD,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC3B,IAAI,CAAiB;IAC9B,YAAY,IAAoB,EAAE,OAAe;QAChD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;CACD;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG;IAC1B,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;IACR,EAAE,EAAE,CAAC;IACL,IAAI,EAAE,CAAC;IACP,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;CACK,CAAC;AAEX,oEAAoE;AACpE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAChC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAEhC,+EAA+E;AAC/E,MAAM,CAAC,MAAM,YAAY,GAAqC;IAC7D,IAAI,EAAE,WAAW,CAAC,IAAI;IACtB,KAAK,EAAE,WAAW,CAAC,KAAK;IACxB,EAAE,EAAE,WAAW,CAAC,EAAE;IAClB,IAAI,EAAE,WAAW,CAAC,IAAI;IACtB,CAAC,EAAE,WAAW,CAAC,CAAC;IAChB,CAAC,EAAE,WAAW,CAAC,CAAC;IAChB,4CAA4C;IAC5C,CAAC,EAAE,WAAW,CAAC,IAAI;IACnB,CAAC,EAAE,WAAW,CAAC,KAAK;IACpB,CAAC,EAAE,WAAW,CAAC,EAAE;IACjB,CAAC,EAAE,WAAW,CAAC,IAAI;IACnB,yEAAyE;IACzE,4EAA4E;IAC5E,CAAC,EAAE,WAAW,CAAC,CAAC;CAChB,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACxC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxC,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACtC,0EAA0E;QAC1E,IAAI,IAAI,CAAC,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,CAAC;AAE5B;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG;IACrB,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;CACE,CAAC;AAIX;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG;IAClB,uEAAuE;IACvE,QAAQ,EAAE,sBAAsB;IAChC,6CAA6C;IAC7C,KAAK,EAAE,kBAAkB;IACzB,4DAA4D;IAC5D,IAAI,EAAE,iBAAiB;IACvB,gDAAgD;IAChD,KAAK,EAAE,kBAAkB;CAChB,CAAC;AAsBX;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,GAAiB;IAC9C,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,8BAA8B;IACxE,QAAQ,GAAG,CAAC,EAAE,EAAE,CAAC;QAChB,KAAK,MAAM;YACV,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC;YACvB,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC;YAC7B,MAAM;QACP,KAAK,OAAO;YACX,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC;YACxB,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;YAC3B,MAAM;QACP,KAAK,MAAM;YACV,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC;YACvB,MAAM;QACP,KAAK,OAAO;YACX,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC;YACxB,MAAM;QACP,KAAK,MAAM;YACV,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC;YACvB,MAAM;IACR,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAC,MAA+B;IAC3D,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;IACvD,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IACxE,OAAO,GAAG,CAAC;AACZ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,YAAY;IAChB,MAAM,GAAa,EAAE,CAAC;IAE9B,0EAA0E;IAC1E,IAAI,CAAC,KAAqC;QACzC,KAAK,MAAM,CAAC,IAAI,KAAK;YAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAClD,MAAM,GAAG,GAAmB,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,UAAU,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;YAChD,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,GAAG,CAAC;IACZ,CAAC;CACD;AAED,6EAA6E;AAC7E,MAAM,UAAU,WAAW,CAAC,KAAwB;IACnD,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC;IAChC,QAAQ,EAAE,EAAE,CAAC;QACZ,KAAK,MAAM,CAAC,IAAI;YACf,OAAO,EAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAC,CAAC;QAClC,KAAK,MAAM,CAAC,KAAK;YAChB,OAAO,EAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAC,CAAC;QACjC,KAAK,MAAM,CAAC,IAAI;YACf,OAAO,EAAC,EAAE,EAAE,MAAM,EAAC,CAAC;QACrB,KAAK,MAAM,CAAC,KAAK;YAChB,OAAO,EAAC,EAAE,EAAE,OAAO,EAAC,CAAC;QACtB,KAAK,MAAM,CAAC,IAAI;YACf,OAAO,EAAC,EAAE,EAAE,MAAM,EAAC,CAAC;QACrB;YACC,2EAA2E;YAC3E,OAAO,EAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAC,CAAC;IACjC,CAAC;AACF,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,SAAS,CAAC,IAAuB;IAChD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;QACvB,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAChC,IAAI,GAAG,GAAG,CAAC;QACX,OAAO,IAAI,CAAC;IACb,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC5B,OAAiC,EACjC,MAAc;IAEd,MAAM,IAAI,GAAG,IAAI,KAAK,CAAS,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/C,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACzB,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAChE,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QACzC,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC7C,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IACtC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,GAAG,IAAI,cAAc,IAAI,GAAG,IAAI,cAAc;YAAE,OAAO,GAAG,CAAC;IAChE,CAAC;IACD,MAAM,IAAI,UAAU,CACnB,wBAAwB,EACxB,mBAAmB,KAAK,4FAA4F,CACpH,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC5C,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,4EAA4E;IAC5E,6EAA6E;IAC7E,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACpE,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QAC/B,MAAM,CAAC,GAAG,yBAAyB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAChB,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvB,MAAM,IAAI,UAAU,CACnB,wBAAwB,EACxB,oBAAoB,GAAG,6FAA6F,CACpH,CAAC;YACH,CAAC;YACD,sCAAsC;YACtC,OAAO,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,gBAAgB,CAAC,GAAG,CAAC,EAAC,CAAC,CAAC;YAC3D,SAAS;QACV,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1B,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACpD,MAAM,GAAG,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAW,CAAC,CAAC;QAC7C,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;YACf,MAAM,IAAI,UAAU,CACnB,wBAAwB,EACxB,mBAAmB,EAAE,mBAAmB,IAAI,QAAQ,GAAG,GAAG,CAC1D,CAAC;QACH,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,EAAC,IAAI,EAAE,EAAE,EAAE,GAAG,EAAC,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,aAAa,GAA6B;IACtD,EAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC,EAAC,EAAE,mBAAmB;IACzD,EAAC,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC,EAAC,EAAE,kBAAkB;IAC1D,EAAC,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC,EAAC,EAAE,8BAA8B;IACtE,EAAC,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,WAAW,CAAC,KAAK,EAAC,EAAE,0BAA0B;IACtE,EAAC,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC,EAAC;IACtC,EAAC,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC,EAAC;CACtC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AACvC,MAAM,CAAC,MAAM,mBAAmB,GAAsB,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;AAqCnE;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC9B,GAAW,EACX,WAAsE,EACtE,OAA4B,IAAI,GAAG,EAAE;IAErC,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,GAAG,CAAC;IAC1C,OAAO,GAAG;SACR,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACb,MAAM,CAAC,GAAG,0BAA0B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,CAAC,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAC5B,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAW,CAAC;QAC5B,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,wBAAwB;QACzD,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,CAAC,6BAA6B;QACrE,OAAO,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,MAAM,aAAa,GAAG,MAAe,CAAC;AAE7C;;;;;;;;GAQG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAe;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,aAAa,CAAC;IAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,oBAAoB,CAAC;IACnD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,UAAU,IAAI,mBAAmB,CAAC,CAAC;IACnE,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE5C,MAAM,MAAM,GAAmB,EAAE,CAAC;IAClC,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAC;IAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACjC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvB,0EAA0E;QAC1E,uEAAuE;QACvE,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;YACpB,MAAM,CAAC,IAAI,CAAC,EAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAC,CAAC,CAAC;YACpC,QAAQ,GAAG,CAAC,CAAC;QACd,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,EAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAC,CAAC,CAAC;QACrC,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,MAAM,CAAC,IAAI,CAAC,EAAC,EAAE,EAAE,MAAM,EAAC,CAAC,CAAC;IAClD,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,EAAC,EAAE,EAAE,MAAM,EAAC,CAAC,CAAC;IAC1B,OAAO,MAAM,CAAC;AACf,CAAC;AAgBD;;;;GAIG;AACH,SAAS,gBAAgB,CAAC,GAAW;IAKpC,OAAO;QACN,MAAM,EAAE,yBAAyB,CAAC,IAAI,CAAC,GAAG,CAAC;QAC3C,QAAQ,EAAE,2BAA2B,CAAC,IAAI,CAAC,GAAG,CAAC;QAC/C,IAAI,EAAE,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC;KACvC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,SAAS,SAAS,CACjB,SAA8D,EAC9D,QAAgB,EAChB,YAAoB;IAEpB,4EAA4E;IAC5E,oEAAoE;IACpE,OAAO;QACN,wEAAwE;QACxE,4EAA4E;QAC5E,4EAA4E;QAC5E,wEAAwE;QACxE,2BAA2B;QAC3B,0DAA0D;QAC1D,YAAY,UAAU,EAAE;QACxB,4FAA4F;QAC5F,qFAAqF;QACrF,6EAA6E;QAC7E,6BAA6B;QAC7B,wCAAwC;QACxC,8DAA8D;QAC9D,6CAA6C;QAC7C,uDAAuD;QACvD,sCAAsC;QACtC,gBAAgB;QAChB,KAAK;QACL,iEAAiE;QACjE,uBAAuB;QACvB,oCAAoC;QACpC,kCAAkC;QAClC,6BAA6B;QAC7B,WAAW,MAAM,CAAC,KAAK,gCAAgC,GAAG,CAAC,KAAK,IAAI;QACpE,6EAA6E;QAC7E,4EAA4E;QAC5E,eAAe,MAAM,CAAC,IAAI,wBAAwB;QAClD,eAAe,MAAM,CAAC,IAAI,sBAAsB,YAAY,uCAAuC,GAAG,CAAC,IAAI,IAAI;QAC/G,eAAe,MAAM,CAAC,KAAK,gCAAgC,GAAG,CAAC,KAAK,IAAI;QACxE,eAAe,MAAM,CAAC,IAAI,iBAAiB,QAAQ,IAAI;QACvD,MAAM;QACN,KAAK;QACL,0EAA0E;QAC1E,sEAAsE;QACtE,4EAA4E;QAC5E,4EAA4E;QAC5E,8EAA8E;QAC9E,8EAA8E;QAC9E,uBAAuB;QACvB,eAAe;QACf,yBAAyB;QACzB,mBAAmB;QACnB,uCAAuC;QACvC,yBAAyB;QACzB,6EAA6E;QAC7E,4EAA4E;QAC5E,oCAAoC,GAAG,CAAC,QAAQ,QAAQ;QACxD,eAAe;QACf,MAAM;QACN,eAAe;QACf,KAAK;QACL,EAAE;KACF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,UAAU,CAAC,SAInB;IACA,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ;QAChC,CAAC,CAAC,WAAW;QACb,CAAC,CAAC,SAAS,CAAC,MAAM;YACjB,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,KAAK,GAAa;QACvB,EAAE;QACF,MAAM;QACN,qEAAqE;QACrE,8EAA8E;KAC9E,CAAC;IACF,4EAA4E;IAC5E,2CAA2C;IAC3C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,cAAc,MAAM,EAAE,CAAC,CAAC;IACpC,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;IACvD,+EAA+E;IAC/E,yEAAyE;IACzE,KAAK,CAAC,IAAI,CACT,oDAAoD;IACpD,6DAA6D;IAC7D,SAAS,CAAC,QAAQ;QACjB,CAAC,CAAC,sDAAsD;QACxD,CAAC,CAAC,EAAE;IACL,yEAAyE;IACzE,yEAAyE;IACzE,8EAA8E;IAC9E,0EAA0E;IAC1E,6CAA6C;IAC7C,kBAAkB,EAClB,+EAA+E,EAC/E,2BAA2B,EAC3B,uEAAuE,EACvE,MAAM,EACN,KAAK,EACL,EAAE,CACF,CAAC;IACF,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AACxD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAChC,QAAgB,EAChB,OAAkB,EAAE;IAEpB,IAAI,IAAU,CAAC;IACf,IAAI,CAAC;QACJ,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,IAAI,CAAC,YAAY,cAAc,EAAE,CAAC;YACjC,MAAM,IAAI,UAAU,CACnB,2BAA2B,EAC3B,wBAAwB,CAAC,CAAC,OAAO,EAAE,CACnC,CAAC;QACH,CAAC;QACD,MAAM,CAAC,CAAC;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,aAAa,CAAC;IAChD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,aAAa,CAAC;IACxD,8EAA8E;IAC9E,6EAA6E;IAC7E,wEAAwE;IACxE,MAAM,OAAO,GAAG,eAAe,CAC9B,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,EAC5B,IAAI,CAAC,WAAW,CAChB,CAAC;IACF,MAAM,SAAS,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAE5C,6EAA6E;IAC7E,+EAA+E;IAC/E,+DAA+D;IAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IAExE,MAAM,GAAG,GAAG;QACX,SAAS,CAAC,SAAS,EAAE,QAAQ,EAAE,YAAY,CAAC;QAC5C,QAAQ,GAAG,OAAO;QAClB,UAAU,CAAC,SAAS,CAAC;KACrB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAE5B,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IAE/D,OAAO;QACN,QAAQ,EAAE,IAAI,CAAC,SAAS,EAAE;QAC1B,MAAM;QACN,MAAM;QACN,YAAY;QACZ,SAAS;KACT,CAAC;AACH,CAAC"}