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,153 @@
1
+ import { CartParseError } from './errors.js';
2
+ /**
3
+ * The section names picopilot models in a text-format `.p8` cart. These are the
4
+ * markers PICO-8 writes (`__lua__`, `__gfx__`, ...); `__label__` is the cart
5
+ * label image, also hex.
6
+ */
7
+ export const CART_SECTION_NAMES = [
8
+ 'lua',
9
+ 'gfx',
10
+ 'gff',
11
+ 'label',
12
+ 'map',
13
+ 'sfx',
14
+ 'music',
15
+ ];
16
+ const KNOWN_SECTIONS = new Set(CART_SECTION_NAMES);
17
+ /**
18
+ * Matches a whole line that is a section marker: `__name__` optionally followed
19
+ * by a `\r` (CRLF) and the line's `\n`, anchored so a `__x__` appearing mid-line
20
+ * (e.g. inside Lua) is never mistaken for a marker. The trailing newline is
21
+ * captured into the match so the marker line, terminator included, is preserved.
22
+ */
23
+ const SECTION_MARKER = /^__([a-zA-Z0-9]+)__[^\S\r\n]*(\r?\n|$)/gm;
24
+ /**
25
+ * The in-memory model of a text-format `.p8` cart: the load-bearing seam every
26
+ * cart-touching command (init, gfx, tokens, verify) reads and writes through.
27
+ *
28
+ * Design contract:
29
+ *
30
+ * - **Round-trip is identity.** `Cart.parse(text).serialize() === text`, byte
31
+ * for byte, for any well-formed cart, regardless of newline style or trailing
32
+ * newline. Achieved by preserving the header, each marker line, and each
33
+ * section body verbatim, and never normalising whitespace on the way through.
34
+ * - **Sections are independently addressable.** {@link getSection} /
35
+ * {@link setSection} read and replace one section without disturbing the
36
+ * others (or the header).
37
+ * - **gfx/map are addressable as regions, not opaque strings.** The `__gfx__`
38
+ * and `__map__` hex bodies are exposed as row/pixel grids (see `gfx.ts` /
39
+ * `map.ts`) so the gfx codec and the 0x1000 overlap check can target specific
40
+ * sprites/map rows.
41
+ */
42
+ export class Cart {
43
+ /**
44
+ * The raw text before the first section marker (the `pico-8 cartridge ...`
45
+ * line, the `version N` line, and any trailing blank lines), preserved
46
+ * verbatim so it round-trips exactly.
47
+ */
48
+ header;
49
+ sections;
50
+ constructor(header, sections) {
51
+ this.header = header;
52
+ this.sections = sections;
53
+ }
54
+ /**
55
+ * Parses a `.p8` text blob into a {@link Cart}.
56
+ *
57
+ * Malformed input throws a {@link CartParseError} with a machine-readable
58
+ * `code` (missing header/version, unknown or duplicate section) rather than
59
+ * crashing, so the command layer can surface a structured failure.
60
+ */
61
+ static parse(text) {
62
+ if (!/^pico-8 cartridge/.test(text)) {
63
+ throw new CartParseError('missing-header', 'not a .p8 cart: expected a "pico-8 cartridge ..." header on the first line');
64
+ }
65
+ if (!/^pico-8 cartridge[^\n]*\r?\nversion\s+\d+/.test(text)) {
66
+ throw new CartParseError('missing-version', 'malformed .p8 cart: expected a "version N" line after the header');
67
+ }
68
+ const markers = [];
69
+ SECTION_MARKER.lastIndex = 0;
70
+ for (let m = SECTION_MARKER.exec(text); m !== null; m = SECTION_MARKER.exec(text)) {
71
+ markers.push({
72
+ name: m[1],
73
+ marker: m[0],
74
+ start: m.index,
75
+ bodyStart: m.index + m[0].length,
76
+ });
77
+ }
78
+ const header = markers.length > 0 ? text.slice(0, markers[0]?.start) : text;
79
+ const sections = [];
80
+ const seen = new Set();
81
+ for (let i = 0; i < markers.length; i++) {
82
+ const cur = markers[i];
83
+ if (cur === undefined)
84
+ continue;
85
+ if (!KNOWN_SECTIONS.has(cur.name)) {
86
+ throw new CartParseError('unknown-section', `unknown .p8 section "__${cur.name}__" (known: ${CART_SECTION_NAMES.map((n) => `__${n}__`).join(', ')})`);
87
+ }
88
+ if (seen.has(cur.name)) {
89
+ throw new CartParseError('duplicate-section', `duplicate .p8 section "__${cur.name}__"`);
90
+ }
91
+ seen.add(cur.name);
92
+ const bodyEnd = markers[i + 1]?.start ?? text.length;
93
+ sections.push({
94
+ name: cur.name,
95
+ marker: cur.marker,
96
+ body: text.slice(cur.bodyStart, bodyEnd),
97
+ });
98
+ }
99
+ return new Cart(header, sections);
100
+ }
101
+ /** The names of the sections present in this cart, in file order. */
102
+ sectionNames() {
103
+ return this.sections.map((s) => s.name);
104
+ }
105
+ /** True if the cart contains the named section. */
106
+ hasSection(name) {
107
+ return this.sections.some((s) => s.name === name);
108
+ }
109
+ /**
110
+ * Returns the raw body text of a section (everything after the marker line,
111
+ * verbatim including its own newlines), or `undefined` if the section is not
112
+ * present. The gfx/map region helpers build on this.
113
+ */
114
+ getSection(name) {
115
+ return this.sections.find((s) => s.name === name)?.body;
116
+ }
117
+ /**
118
+ * Replaces one section's body in place, leaving every other section and the
119
+ * header untouched. If the section is not yet present it is appended (with a
120
+ * `__name__\n` marker in the file's dominant newline style).
121
+ *
122
+ * The body is written verbatim; callers that want a trailing newline must
123
+ * include it. Region-level helpers (gfx/map) go through here.
124
+ */
125
+ setSection(name, body) {
126
+ const existing = this.sections.find((s) => s.name === name);
127
+ if (existing !== undefined) {
128
+ existing.body = body;
129
+ return;
130
+ }
131
+ this.sections.push({ name, marker: `__${name}__${this.newline()}`, body });
132
+ }
133
+ /**
134
+ * Serializes the cart back to a `.p8` text blob. For an unmodified cart this
135
+ * is byte-identical to the text it was parsed from.
136
+ */
137
+ serialize() {
138
+ let out = this.header;
139
+ for (const s of this.sections) {
140
+ out += s.marker + s.body;
141
+ }
142
+ return out;
143
+ }
144
+ /**
145
+ * The dominant newline style of the file, inferred from the header, used only
146
+ * when APPENDING a brand-new section marker (existing markers keep their own
147
+ * terminator verbatim). Defaults to `\n`.
148
+ */
149
+ newline() {
150
+ return /\r\n/.test(this.header) ? '\r\n' : '\n';
151
+ }
152
+ }
153
+ //# sourceMappingURL=cart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cart.js","sourceRoot":"","sources":["../../../src/engine/cart/cart.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Structured errors for the cart model.
3
+ *
4
+ * Parsing a malformed cart throws a `CartParseError` (never an unstructured
5
+ * crash), carrying a machine-readable `code` and a human-readable `message` so
6
+ * the command layer can turn it into incur's `error({ code, message, ... })`
7
+ * envelope rather than letting a raw exception escape. Region access out of
8
+ * bounds throws a `CartRangeError` for the same reason.
9
+ */
10
+ /** Machine-readable reason a `.p8` could not be parsed into the cart model. */
11
+ export type CartParseErrorCode =
12
+ /** The `pico-8 cartridge ...` header line is missing or unrecognised. */
13
+ 'missing-header'
14
+ /** The `version N` line is missing or malformed. */
15
+ | 'missing-version'
16
+ /** A `__section__` marker is present but names a section we do not model. */
17
+ | 'unknown-section'
18
+ /** The same `__section__` marker appears more than once. */
19
+ | 'duplicate-section';
20
+ /** Thrown when a `.p8` text blob cannot be parsed into a {@link Cart}. */
21
+ export declare class CartParseError extends Error {
22
+ readonly code: CartParseErrorCode;
23
+ constructor(code: CartParseErrorCode, message: string);
24
+ }
25
+ /** Thrown when a gfx/map region access falls outside the addressable grid. */
26
+ export declare class CartRangeError extends Error {
27
+ constructor(message: string);
28
+ }
29
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/engine/cart/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,+EAA+E;AAC/E,MAAM,MAAM,kBAAkB;AAC7B,yEAAyE;AACvE,gBAAgB;AAClB,oDAAoD;GAClD,iBAAiB;AACnB,6EAA6E;GAC3E,iBAAiB;AACnB,4DAA4D;GAC1D,mBAAmB,CAAC;AAEvB,0EAA0E;AAC1E,qBAAa,cAAe,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;gBAEtB,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM;CAKrD;AAED,8EAA8E;AAC9E,qBAAa,cAAe,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI3B"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Structured errors for the cart model.
3
+ *
4
+ * Parsing a malformed cart throws a `CartParseError` (never an unstructured
5
+ * crash), carrying a machine-readable `code` and a human-readable `message` so
6
+ * the command layer can turn it into incur's `error({ code, message, ... })`
7
+ * envelope rather than letting a raw exception escape. Region access out of
8
+ * bounds throws a `CartRangeError` for the same reason.
9
+ */
10
+ /** Thrown when a `.p8` text blob cannot be parsed into a {@link Cart}. */
11
+ export class CartParseError extends Error {
12
+ code;
13
+ constructor(code, message) {
14
+ super(message);
15
+ this.name = 'CartParseError';
16
+ this.code = code;
17
+ }
18
+ }
19
+ /** Thrown when a gfx/map region access falls outside the addressable grid. */
20
+ export class CartRangeError extends Error {
21
+ constructor(message) {
22
+ super(message);
23
+ this.name = 'CartRangeError';
24
+ }
25
+ }
26
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/engine/cart/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,0EAA0E;AAC1E,MAAM,OAAO,cAAe,SAAQ,KAAK;IAC/B,IAAI,CAAqB;IAElC,YAAY,IAAwB,EAAE,OAAe;QACpD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;CACD;AAED,8EAA8E;AAC9E,MAAM,OAAO,cAAe,SAAQ,KAAK;IACxC,YAAY,OAAe;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC9B,CAAC;CACD"}
@@ -0,0 +1,79 @@
1
+ import type { Cart } from './cart.js';
2
+ /**
3
+ * Addressable view over a cart's `__gfx__` section: the 128x128 spritesheet as a
4
+ * grid of per-pixel colour nibbles (0..15), NOT an opaque hex string.
5
+ *
6
+ * The spritesheet holds 256 8x8 sprites laid out 16 per row: sprite `n` lives at
7
+ * pixel column `(n % 16) * 8`, row `(n \ 16) * 8`. Sprites 0..127 are the base
8
+ * bank; sprites 128..255 (`0x1000` in memory) alias `__map__` rows 32..63 (the
9
+ * overlap the smart-refuse depends on); see {@link mapRowsForSprite}.
10
+ *
11
+ * Reads/writes go through the parsed grid; call {@link commit} to write the grid
12
+ * back into the cart's `__gfx__` body (preserving its trailing-newline style).
13
+ * If the section body is shorter than the full sheet (a partial/authored cart),
14
+ * missing pixels read as 0 and short rows are padded to full width on commit.
15
+ */
16
+ export declare const GFX_WIDTH = 128;
17
+ export declare const GFX_HEIGHT = 128;
18
+ export declare const SPRITE_SIZE = 8;
19
+ export declare const SPRITES_PER_ROW = 16;
20
+ export declare class GfxSheet {
21
+ /** `pixels[y][x]` is the colour index 0..15 at pixel (x, y). */
22
+ private readonly pixels;
23
+ /** The newline that terminates each row line in the source body (`\n` default). */
24
+ private readonly newline;
25
+ private constructor();
26
+ /**
27
+ * Builds an addressable sheet from a cart's `__gfx__` body. A missing or empty
28
+ * section yields an all-zero sheet, so the codec can still address any sprite.
29
+ */
30
+ static fromBody(body: string | undefined): GfxSheet;
31
+ /** Reads the colour index (0..15) at pixel (x, y). */
32
+ getPixel(x: number, y: number): number;
33
+ /** Writes the colour index (0..15) at pixel (x, y). */
34
+ setPixel(x: number, y: number, color: number): void;
35
+ /**
36
+ * Reads one 8x8 sprite (0..255) as an 8-row grid of colour indices, `grid[r][c]`.
37
+ */
38
+ getSprite(n: number): number[][];
39
+ /**
40
+ * Writes one 8x8 sprite (0..255) from an 8-row grid of colour indices. The
41
+ * grid must be 8x8; each cell 0..15.
42
+ */
43
+ setSprite(n: number, grid: number[][]): void;
44
+ /**
45
+ * Serializes the grid back to a `__gfx__` body (128 lines of 128 hex nibbles),
46
+ * using the source body's newline style and terminating the last line, so it
47
+ * matches what PICO-8 writes.
48
+ */
49
+ toBody(): string;
50
+ /** Writes the grid back into the cart's `__gfx__` section. */
51
+ commit(cart: Cart): void;
52
+ private assertPixel;
53
+ }
54
+ /** The top-left pixel origin of sprite `n` (0..255) in the 128x128 sheet. */
55
+ export declare function spriteOrigin(n: number): {
56
+ x0: number;
57
+ y0: number;
58
+ };
59
+ /**
60
+ * Whether sprite `n` lives in the shared upper bank (128..255) that aliases the
61
+ * bottom half of `__map__`. The gfx/map overlap smart-refuse (next task) keys
62
+ * off this.
63
+ */
64
+ export declare function spriteAliasesMap(n: number): boolean;
65
+ /**
66
+ * The `__map__` rows a shared-bank sprite (128..255) aliases, or `undefined` for
67
+ * a base-bank sprite (0..127) that aliases nothing.
68
+ *
69
+ * Memory layout: sprite `n`'s 8 pixel rows sit at gfx rows `(n \ 16) * 8`; for
70
+ * the shared bank those are gfx rows 64..127, which occupy the SAME memory as
71
+ * `__map__` rows 32..63. Each map row is 128 bytes = 2 gfx pixel rows (128 gfx
72
+ * nibbles map to 128/... ). Concretely: gfx row `g` (64..127) aliases map row
73
+ * `32 + (g - 64) \ 2`, so a single 8-row sprite covers 4 map rows.
74
+ */
75
+ export declare function mapRowsForSprite(n: number): {
76
+ start: number;
77
+ end: number;
78
+ } | undefined;
79
+ //# sourceMappingURL=gfx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gfx.d.ts","sourceRoot":"","sources":["../../../src/engine/cart/gfx.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAGpC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,SAAS,MAAM,CAAC;AAC7B,eAAO,MAAM,UAAU,MAAM,CAAC;AAC9B,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC,qBAAa,QAAQ;IACpB,gEAAgE;IAChE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IAEpC,mFAAmF;IACnF,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,OAAO;IAKP;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ;IAgBnD,sDAAsD;IACtD,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;IAKtC,uDAAuD;IACvD,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IASnD;;OAEG;IACH,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE;IAYhC;;;OAGG;IACH,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,IAAI;IAmB5C;;;;OAIG;IACH,MAAM,IAAI,MAAM;IAWhB,8DAA8D;IAC9D,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAIxB,OAAO,CAAC,WAAW;CAcnB;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAC,CAQhE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAKnD;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC/B,CAAC,EAAE,MAAM,GACP;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAC,GAAG,SAAS,CAM1C"}
@@ -0,0 +1,171 @@
1
+ import { CartRangeError } from './errors.js';
2
+ /**
3
+ * Addressable view over a cart's `__gfx__` section: the 128x128 spritesheet as a
4
+ * grid of per-pixel colour nibbles (0..15), NOT an opaque hex string.
5
+ *
6
+ * The spritesheet holds 256 8x8 sprites laid out 16 per row: sprite `n` lives at
7
+ * pixel column `(n % 16) * 8`, row `(n \ 16) * 8`. Sprites 0..127 are the base
8
+ * bank; sprites 128..255 (`0x1000` in memory) alias `__map__` rows 32..63 (the
9
+ * overlap the smart-refuse depends on); see {@link mapRowsForSprite}.
10
+ *
11
+ * Reads/writes go through the parsed grid; call {@link commit} to write the grid
12
+ * back into the cart's `__gfx__` body (preserving its trailing-newline style).
13
+ * If the section body is shorter than the full sheet (a partial/authored cart),
14
+ * missing pixels read as 0 and short rows are padded to full width on commit.
15
+ */
16
+ export const GFX_WIDTH = 128;
17
+ export const GFX_HEIGHT = 128;
18
+ export const SPRITE_SIZE = 8;
19
+ export const SPRITES_PER_ROW = 16;
20
+ export class GfxSheet {
21
+ /** `pixels[y][x]` is the colour index 0..15 at pixel (x, y). */
22
+ pixels;
23
+ /** The newline that terminates each row line in the source body (`\n` default). */
24
+ newline;
25
+ constructor(pixels, newline) {
26
+ this.pixels = pixels;
27
+ this.newline = newline;
28
+ }
29
+ /**
30
+ * Builds an addressable sheet from a cart's `__gfx__` body. A missing or empty
31
+ * section yields an all-zero sheet, so the codec can still address any sprite.
32
+ */
33
+ static fromBody(body) {
34
+ const newline = body !== undefined && /\r\n/.test(body) ? '\r\n' : '\n';
35
+ const pixels = [];
36
+ const lines = body === undefined ? [] : body.split(/\r?\n/);
37
+ for (let y = 0; y < GFX_HEIGHT; y++) {
38
+ const line = lines[y] ?? '';
39
+ const row = [];
40
+ for (let x = 0; x < GFX_WIDTH; x++) {
41
+ const ch = line[x];
42
+ row.push(ch === undefined ? 0 : hexNibble(ch));
43
+ }
44
+ pixels.push(row);
45
+ }
46
+ return new GfxSheet(pixels, newline);
47
+ }
48
+ /** Reads the colour index (0..15) at pixel (x, y). */
49
+ getPixel(x, y) {
50
+ this.assertPixel(x, y);
51
+ return this.pixels[y]?.[x] ?? 0;
52
+ }
53
+ /** Writes the colour index (0..15) at pixel (x, y). */
54
+ setPixel(x, y, color) {
55
+ this.assertPixel(x, y);
56
+ if (!Number.isInteger(color) || color < 0 || color > 15) {
57
+ throw new CartRangeError(`colour ${color} out of range 0..15`);
58
+ }
59
+ const row = this.pixels[y];
60
+ if (row !== undefined)
61
+ row[x] = color;
62
+ }
63
+ /**
64
+ * Reads one 8x8 sprite (0..255) as an 8-row grid of colour indices, `grid[r][c]`.
65
+ */
66
+ getSprite(n) {
67
+ const { x0, y0 } = spriteOrigin(n);
68
+ const grid = [];
69
+ for (let r = 0; r < SPRITE_SIZE; r++) {
70
+ const row = [];
71
+ for (let c = 0; c < SPRITE_SIZE; c++)
72
+ row.push(this.getPixel(x0 + c, y0 + r));
73
+ grid.push(row);
74
+ }
75
+ return grid;
76
+ }
77
+ /**
78
+ * Writes one 8x8 sprite (0..255) from an 8-row grid of colour indices. The
79
+ * grid must be 8x8; each cell 0..15.
80
+ */
81
+ setSprite(n, grid) {
82
+ if (grid.length !== SPRITE_SIZE) {
83
+ throw new CartRangeError(`sprite grid must have ${SPRITE_SIZE} rows, got ${grid.length}`);
84
+ }
85
+ const { x0, y0 } = spriteOrigin(n);
86
+ for (let r = 0; r < SPRITE_SIZE; r++) {
87
+ const gridRow = grid[r];
88
+ if (gridRow === undefined || gridRow.length !== SPRITE_SIZE) {
89
+ throw new CartRangeError(`sprite grid row ${r} must have ${SPRITE_SIZE} cells`);
90
+ }
91
+ for (let c = 0; c < SPRITE_SIZE; c++)
92
+ this.setPixel(x0 + c, y0 + r, gridRow[c] ?? 0);
93
+ }
94
+ }
95
+ /**
96
+ * Serializes the grid back to a `__gfx__` body (128 lines of 128 hex nibbles),
97
+ * using the source body's newline style and terminating the last line, so it
98
+ * matches what PICO-8 writes.
99
+ */
100
+ toBody() {
101
+ const lines = [];
102
+ for (let y = 0; y < GFX_HEIGHT; y++) {
103
+ let line = '';
104
+ for (let x = 0; x < GFX_WIDTH; x++)
105
+ line += (this.pixels[y]?.[x] ?? 0).toString(16);
106
+ lines.push(line);
107
+ }
108
+ return lines.join(this.newline) + this.newline;
109
+ }
110
+ /** Writes the grid back into the cart's `__gfx__` section. */
111
+ commit(cart) {
112
+ cart.setSection('gfx', this.toBody());
113
+ }
114
+ assertPixel(x, y) {
115
+ if (!Number.isInteger(x) ||
116
+ !Number.isInteger(y) ||
117
+ x < 0 ||
118
+ y < 0 ||
119
+ x >= GFX_WIDTH ||
120
+ y >= GFX_HEIGHT) {
121
+ throw new CartRangeError(`pixel (${x}, ${y}) out of range 0..${GFX_WIDTH - 1}`);
122
+ }
123
+ }
124
+ }
125
+ /** The top-left pixel origin of sprite `n` (0..255) in the 128x128 sheet. */
126
+ export function spriteOrigin(n) {
127
+ if (!Number.isInteger(n) || n < 0 || n > 255) {
128
+ throw new CartRangeError(`sprite ${n} out of range 0..255`);
129
+ }
130
+ return {
131
+ x0: (n % SPRITES_PER_ROW) * SPRITE_SIZE,
132
+ y0: Math.floor(n / SPRITES_PER_ROW) * SPRITE_SIZE,
133
+ };
134
+ }
135
+ /**
136
+ * Whether sprite `n` lives in the shared upper bank (128..255) that aliases the
137
+ * bottom half of `__map__`. The gfx/map overlap smart-refuse (next task) keys
138
+ * off this.
139
+ */
140
+ export function spriteAliasesMap(n) {
141
+ if (!Number.isInteger(n) || n < 0 || n > 255) {
142
+ throw new CartRangeError(`sprite ${n} out of range 0..255`);
143
+ }
144
+ return n >= 128;
145
+ }
146
+ /**
147
+ * The `__map__` rows a shared-bank sprite (128..255) aliases, or `undefined` for
148
+ * a base-bank sprite (0..127) that aliases nothing.
149
+ *
150
+ * Memory layout: sprite `n`'s 8 pixel rows sit at gfx rows `(n \ 16) * 8`; for
151
+ * the shared bank those are gfx rows 64..127, which occupy the SAME memory as
152
+ * `__map__` rows 32..63. Each map row is 128 bytes = 2 gfx pixel rows (128 gfx
153
+ * nibbles map to 128/... ). Concretely: gfx row `g` (64..127) aliases map row
154
+ * `32 + (g - 64) \ 2`, so a single 8-row sprite covers 4 map rows.
155
+ */
156
+ export function mapRowsForSprite(n) {
157
+ if (!spriteAliasesMap(n))
158
+ return undefined;
159
+ const { y0 } = spriteOrigin(n);
160
+ // y0 is 64..120 for the shared bank; each pair of gfx rows aliases one map row.
161
+ const start = 32 + Math.floor((y0 - 64) / 2);
162
+ return { start, end: start + SPRITE_SIZE / 2 }; // 8 gfx rows = 4 map rows: [start, end)
163
+ }
164
+ function hexNibble(ch) {
165
+ const v = Number.parseInt(ch, 16);
166
+ if (Number.isNaN(v)) {
167
+ throw new CartRangeError(`invalid hex nibble "${ch}" in __gfx__`);
168
+ }
169
+ return v;
170
+ }
171
+ //# sourceMappingURL=gfx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gfx.js","sourceRoot":"","sources":["../../../src/engine/cart/gfx.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The picopilot cart model: parse a text-format `.p8` into a typed, section-level
3
+ * model, read/modify individual sections, address the `__gfx__`/`__map__` hex as
4
+ * pixel/tile regions, and serialize back byte-identically.
5
+ *
6
+ * This is the load-bearing seam every cart-touching command (init, gfx, tokens,
7
+ * verify) reads and writes through. See `cart.ts` for the round-trip-identity
8
+ * contract; `gfx.ts` / `map.ts` for the addressable regions (and the 0x1000
9
+ * overlap the smart-refuse depends on).
10
+ */
11
+ export { Cart, CART_SECTION_NAMES, type CartSectionName } from './cart.js';
12
+ export { CartParseError, type CartParseErrorCode, CartRangeError, } from './errors.js';
13
+ export { GfxSheet, GFX_HEIGHT, GFX_WIDTH, mapRowsForSprite, SPRITE_SIZE, SPRITES_PER_ROW, spriteAliasesMap, spriteOrigin, } from './gfx.js';
14
+ export { MapData, MAP_ROWS, MAP_WIDTH } from './map.js';
15
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/engine/cart/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAC,IAAI,EAAE,kBAAkB,EAAE,KAAK,eAAe,EAAC,MAAM,WAAW,CAAC;AACzE,OAAO,EACN,cAAc,EACd,KAAK,kBAAkB,EACvB,cAAc,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EACN,QAAQ,EACR,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,YAAY,GACZ,MAAM,UAAU,CAAC;AAClB,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAC,MAAM,UAAU,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The picopilot cart model: parse a text-format `.p8` into a typed, section-level
3
+ * model, read/modify individual sections, address the `__gfx__`/`__map__` hex as
4
+ * pixel/tile regions, and serialize back byte-identically.
5
+ *
6
+ * This is the load-bearing seam every cart-touching command (init, gfx, tokens,
7
+ * verify) reads and writes through. See `cart.ts` for the round-trip-identity
8
+ * contract; `gfx.ts` / `map.ts` for the addressable regions (and the 0x1000
9
+ * overlap the smart-refuse depends on).
10
+ */
11
+ export { Cart, CART_SECTION_NAMES } from './cart.js';
12
+ export { CartParseError, CartRangeError, } from './errors.js';
13
+ export { GfxSheet, GFX_HEIGHT, GFX_WIDTH, mapRowsForSprite, SPRITE_SIZE, SPRITES_PER_ROW, spriteAliasesMap, spriteOrigin, } from './gfx.js';
14
+ export { MapData, MAP_ROWS, MAP_WIDTH } from './map.js';
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/engine/cart/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAC,IAAI,EAAE,kBAAkB,EAAuB,MAAM,WAAW,CAAC;AACzE,OAAO,EACN,cAAc,EAEd,cAAc,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EACN,QAAQ,EACR,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,YAAY,GACZ,MAAM,UAAU,CAAC;AAClB,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAC,MAAM,UAAU,CAAC"}
@@ -0,0 +1,46 @@
1
+ import type { Cart } from './cart.js';
2
+ /**
3
+ * Addressable view over a cart's `__map__` section: the tile map as a grid of
4
+ * 8-bit tile indices, NOT an opaque hex string.
5
+ *
6
+ * The text `__map__` stores the base map: up to 32 rows of 128 tiles, each tile
7
+ * a byte written as two hex chars (`grid[y][x]`). Rows 32..63 (the shared half)
8
+ * are NOT stored here in the text format (they live in `__gfx__`'s upper bank
9
+ ), see `gfx.ts` {@link mapRowsForSprite}; this view models the `__map__` body
10
+ * as written. The 0x1000 overlap check reads a shared-bank sprite's aliased map
11
+ * region through {@link nonZeroTilesInRows} to decide whether a write clobbers
12
+ * real tiles.
13
+ *
14
+ * Reads/writes go through the parsed grid; call {@link commit} to write it back
15
+ * into the cart's `__map__` body. A missing/short section reads as zero tiles.
16
+ */
17
+ export declare const MAP_WIDTH = 128;
18
+ export declare const MAP_ROWS = 32;
19
+ export declare class MapData {
20
+ /** `tiles[y][x]` is the 0..255 tile index at map cell (x, y). */
21
+ private readonly tiles;
22
+ private readonly newline;
23
+ private constructor();
24
+ /** Builds an addressable map from a cart's `__map__` body (all-zero if absent). */
25
+ static fromBody(body: string | undefined): MapData;
26
+ /** Reads the tile index (0..255) at map cell (x, y). */
27
+ getTile(x: number, y: number): number;
28
+ /** Writes the tile index (0..255) at map cell (x, y). */
29
+ setTile(x: number, y: number, value: number): void;
30
+ /** Reads one full map row (128 tile indices) at row `y`. */
31
+ getRow(y: number): number[];
32
+ /**
33
+ * Counts non-zero tiles across a half-open row range `[start, end)`, clamped
34
+ * to the stored `__map__` rows (0..31). Shared-bank rows (32..63) that the
35
+ * text `__map__` does not store contribute zero. This is the primitive the
36
+ * gfx/map overlap smart-refuse uses to decide whether a shared-bank sprite
37
+ * write would clobber real map data.
38
+ */
39
+ nonZeroTilesInRows(start: number, end: number): number;
40
+ /** Serializes the grid back to a `__map__` body (32 lines of 256 hex chars). */
41
+ toBody(): string;
42
+ /** Writes the grid back into the cart's `__map__` section. */
43
+ commit(cart: Cart): void;
44
+ private assertCell;
45
+ }
46
+ //# sourceMappingURL=map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../../src/engine/cart/map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAGpC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,MAAM,CAAC;AAC7B,eAAO,MAAM,QAAQ,KAAK,CAAC;AAE3B,qBAAa,OAAO;IACnB,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IAEnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,OAAO;IAKP,mFAAmF;IACnF,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO;IAiBlD,wDAAwD;IACxD,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;IAKrC,yDAAyD;IACzD,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IASlD,4DAA4D;IAC5D,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAO3B;;;;;;OAMG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAStD,gFAAgF;IAChF,MAAM,IAAI,MAAM;IAWhB,8DAA8D;IAC9D,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAIxB,OAAO,CAAC,UAAU;CAclB"}