emoemu 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 (213) hide show
  1. package/.claude/settings.local.json +77 -0
  2. package/.node-version +1 -0
  3. package/CLAUDE.md +435 -0
  4. package/README.md +404 -0
  5. package/TODO.md +655 -0
  6. package/dist/index.cjs +25108 -0
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.js +25085 -0
  9. package/docs/building-libretro-cores-arm-mac.md +237 -0
  10. package/docs/config-file-format.md +488 -0
  11. package/docs/cores-trd.md +425 -0
  12. package/docs/headless-hardware-rendering-trd.md +676 -0
  13. package/docs/libretro-cores-trd.md +997 -0
  14. package/docs/mupen64-software-rendering-trd.md +751 -0
  15. package/docs/n64-support-trd.md +306 -0
  16. package/docs/native-rendering-trd.md +540 -0
  17. package/docs/native-ui-rendering-trd.md +1195 -0
  18. package/docs/netplay-trd.md +665 -0
  19. package/docs/retroarch-netplay-docs.md +277 -0
  20. package/docs/save-state-format.md +666 -0
  21. package/eslint.config.js +111 -0
  22. package/icon/icon.png +0 -0
  23. package/icon/icon.pxd +0 -0
  24. package/package.json +63 -0
  25. package/pnpm-workspace.yaml +10 -0
  26. package/src/Emulator/consts.ts +14 -0
  27. package/src/Emulator/index.ts +2496 -0
  28. package/src/Emulator/saveState/index.ts +155 -0
  29. package/src/Emulator/screenshot/index.ts +160 -0
  30. package/src/Emulator/terminalDimensions/index.ts +79 -0
  31. package/src/Emulator/types.ts +83 -0
  32. package/src/cli/commands/consts.ts +10 -0
  33. package/src/cli/commands/index.ts +462 -0
  34. package/src/cli/parseArgs/consts.ts +17 -0
  35. package/src/cli/parseArgs/index.ts +457 -0
  36. package/src/cli/parseArgs/types.ts +61 -0
  37. package/src/cli/runEmulator/index.ts +406 -0
  38. package/src/cli/runEmulator/types.ts +7 -0
  39. package/src/consts.ts +19 -0
  40. package/src/core/button/consts.ts +35 -0
  41. package/src/core/button/index.ts +123 -0
  42. package/src/core/core.ts +300 -0
  43. package/src/core/index.ts +19 -0
  44. package/src/cores/libretro/api/index.ts +334 -0
  45. package/src/cores/libretro/api/types.ts +148 -0
  46. package/src/cores/libretro/callbacks/consts.ts +41 -0
  47. package/src/cores/libretro/callbacks/index.ts +456 -0
  48. package/src/cores/libretro/consts.ts +45 -0
  49. package/src/cores/libretro/coreOptions/consts.ts +36 -0
  50. package/src/cores/libretro/coreOptions/index.ts +222 -0
  51. package/src/cores/libretro/environment/consts.ts +118 -0
  52. package/src/cores/libretro/environment/index.ts +1095 -0
  53. package/src/cores/libretro/index.ts +937 -0
  54. package/src/cores/libretro/loader/index.ts +496 -0
  55. package/src/cores/libretro/pixelFormat/consts.ts +43 -0
  56. package/src/cores/libretro/pixelFormat/index.ts +397 -0
  57. package/src/cores/libretro/types.ts +339 -0
  58. package/src/frontend/AudioManager/index.ts +420 -0
  59. package/src/frontend/SettingsManager/index.ts +250 -0
  60. package/src/frontend/config/index.ts +608 -0
  61. package/src/frontend/config/tests.ts +354 -0
  62. package/src/frontend/config/types.ts +36 -0
  63. package/src/frontend/consts.ts +114 -0
  64. package/src/frontend/coreBuilder/index.ts +644 -0
  65. package/src/frontend/coreBuilder/types.ts +15 -0
  66. package/src/frontend/coreDownloader/index.ts +620 -0
  67. package/src/frontend/coreDownloader/types.ts +17 -0
  68. package/src/frontend/corePreferences/index.ts +69 -0
  69. package/src/frontend/coreRegistry/index.ts +276 -0
  70. package/src/frontend/directoryCache/index.ts +75 -0
  71. package/src/frontend/index.ts +79 -0
  72. package/src/frontend/notifications/consts.ts +14 -0
  73. package/src/frontend/notifications/index.ts +250 -0
  74. package/src/frontend/playlist/consts.ts +168 -0
  75. package/src/frontend/playlist/index.ts +899 -0
  76. package/src/frontend/playlist/labelFormatter/consts.ts +15 -0
  77. package/src/frontend/playlist/labelFormatter/index.ts +155 -0
  78. package/src/frontend/playlist/labelFormatter/tests.ts +153 -0
  79. package/src/frontend/playlist/reader/index.ts +559 -0
  80. package/src/frontend/playlist/sync/index.ts +511 -0
  81. package/src/frontend/playlist/systemLookup/index.ts +233 -0
  82. package/src/frontend/playlist/utils/index.ts +50 -0
  83. package/src/frontend/romScanner/consts.ts +348 -0
  84. package/src/frontend/romScanner/index.ts +1957 -0
  85. package/src/frontend/saveServices/consts.ts +2 -0
  86. package/src/frontend/saveServices/index.ts +313 -0
  87. package/src/frontend/serviceProvider/index.ts +108 -0
  88. package/src/frontend/serviceProvider/types.ts +13 -0
  89. package/src/index.ts +428 -0
  90. package/src/input/Controller/consts.ts +50 -0
  91. package/src/input/Controller/index.ts +81 -0
  92. package/src/input/GamepadManager/consts.ts +22 -0
  93. package/src/input/GamepadManager/index.ts +418 -0
  94. package/src/input/InputManager/consts.ts +86 -0
  95. package/src/input/InputManager/index.ts +593 -0
  96. package/src/input/InputMapper/consts.ts +33 -0
  97. package/src/input/InputMapper/index.ts +436 -0
  98. package/src/input/consts.ts +410 -0
  99. package/src/input/gamepadProfiles/index.ts +1100 -0
  100. package/src/input/index.ts +38 -0
  101. package/src/input/inputUtils/index.ts +31 -0
  102. package/src/netplay/FrameBuffer/consts.ts +2 -0
  103. package/src/netplay/FrameBuffer/index.ts +364 -0
  104. package/src/netplay/FrameBuffer/tests.ts +286 -0
  105. package/src/netplay/InputBuffer/consts.ts +7 -0
  106. package/src/netplay/InputBuffer/index.ts +347 -0
  107. package/src/netplay/InputBuffer/tests.ts +166 -0
  108. package/src/netplay/NetplayClient/index.ts +976 -0
  109. package/src/netplay/NetplayConnection/index.ts +536 -0
  110. package/src/netplay/NetplayDiscovery/consts.ts +41 -0
  111. package/src/netplay/NetplayDiscovery/index.ts +525 -0
  112. package/src/netplay/NetplayServer/index.ts +1407 -0
  113. package/src/netplay/SyncManager/index.ts +984 -0
  114. package/src/netplay/SyncManager/tests.ts +419 -0
  115. package/src/netplay/consts.ts +371 -0
  116. package/src/netplay/crc32/consts.ts +14 -0
  117. package/src/netplay/crc32/index.ts +97 -0
  118. package/src/netplay/crc32/tests.ts +40 -0
  119. package/src/netplay/index.ts +41 -0
  120. package/src/netplay/netplayLogger/consts.ts +30 -0
  121. package/src/netplay/netplayLogger/index.ts +345 -0
  122. package/src/netplay/protocol/consts.ts +86 -0
  123. package/src/netplay/protocol/index.ts +1280 -0
  124. package/src/netplay/protocol/tests.ts +606 -0
  125. package/src/netplay/protocol/types.ts +20 -0
  126. package/src/netplay/types.ts +395 -0
  127. package/src/rendering/KittyRenderer/index.ts +616 -0
  128. package/src/rendering/NativeRenderer/index.ts +279 -0
  129. package/src/rendering/NativeRenderer/tests.ts +133 -0
  130. package/src/rendering/TerminalRenderer/index.ts +770 -0
  131. package/src/rendering/consts.ts +401 -0
  132. package/src/rendering/fonts/CozetteVector.ttf +0 -0
  133. package/src/rendering/index.ts +26 -0
  134. package/src/rendering/nativeUi/NativeWindowManager/index.ts +158 -0
  135. package/src/rendering/nativeUi/NativeWindowManager/tests.ts +81 -0
  136. package/src/rendering/nativeUi/consts.ts +6 -0
  137. package/src/rendering/nativeUi/index.ts +20 -0
  138. package/src/rendering/postProcessing/consts.ts +38 -0
  139. package/src/rendering/postProcessing/index.ts +923 -0
  140. package/src/rendering/shared/ansi/consts.ts +12 -0
  141. package/src/rendering/shared/ansi/index.ts +104 -0
  142. package/src/rendering/shared/consts.ts +2 -0
  143. package/src/rendering/shared/fitToTerminal/index.ts +67 -0
  144. package/src/ui/AddRomsPrompt/consts.ts +13 -0
  145. package/src/ui/AddRomsPrompt/index.tsx +781 -0
  146. package/src/ui/App/consts.ts +2 -0
  147. package/src/ui/App/index.tsx +456 -0
  148. package/src/ui/AppCapabilities/index.tsx +67 -0
  149. package/src/ui/ConfigContext/index.tsx +56 -0
  150. package/src/ui/CoreManager/consts.ts +11 -0
  151. package/src/ui/CoreManager/index.tsx +779 -0
  152. package/src/ui/CoreSelector/consts.ts +2 -0
  153. package/src/ui/CoreSelector/index.tsx +251 -0
  154. package/src/ui/DialogContainer/index.tsx +42 -0
  155. package/src/ui/DialogOptionsList/index.tsx +61 -0
  156. package/src/ui/DuplicateCrcPrompt/consts.ts +5 -0
  157. package/src/ui/DuplicateCrcPrompt/index.tsx +146 -0
  158. package/src/ui/GamepadContext/consts.ts +15 -0
  159. package/src/ui/GamepadContext/index.tsx +295 -0
  160. package/src/ui/NativeDialog/index.tsx +120 -0
  161. package/src/ui/NetplayDisconnectedDialog/index.tsx +93 -0
  162. package/src/ui/NetplayPauseMenu/consts.ts +2 -0
  163. package/src/ui/NetplayPauseMenu/index.tsx +97 -0
  164. package/src/ui/RomBrowser/NetplayPanel/consts.ts +24 -0
  165. package/src/ui/RomBrowser/NetplayPanel/index.tsx +520 -0
  166. package/src/ui/RomBrowser/SettingsPanel/index.tsx +478 -0
  167. package/src/ui/RomBrowser/consts.ts +61 -0
  168. package/src/ui/RomBrowser/index.tsx +1164 -0
  169. package/src/ui/RomBrowser/settingsConfig/index.ts +320 -0
  170. package/src/ui/RomBrowser/types.ts +67 -0
  171. package/src/ui/SaveStateDialog/consts.ts +2 -0
  172. package/src/ui/SaveStateDialog/index.tsx +225 -0
  173. package/src/ui/WarningDialog/index.tsx +113 -0
  174. package/src/ui/consts.ts +27 -0
  175. package/src/ui/hooks/useClearTerminal/consts.ts +2 -0
  176. package/src/ui/hooks/useClearTerminal/index.ts +37 -0
  177. package/src/ui/hooks/useDialogNavigation/index.ts +99 -0
  178. package/src/ui/hooks/useGamepad/consts.ts +21 -0
  179. package/src/ui/hooks/useGamepad/index.ts +194 -0
  180. package/src/ui/index.ts +27 -0
  181. package/src/utils/buffer/consts.ts +17 -0
  182. package/src/utils/buffer/index.ts +129 -0
  183. package/src/utils/color/consts.ts +58 -0
  184. package/src/utils/color/index.ts +183 -0
  185. package/src/utils/compression/consts.ts +50 -0
  186. package/src/utils/compression/index.ts +101 -0
  187. package/src/utils/consts.ts +2 -0
  188. package/src/utils/crc32/consts.ts +22 -0
  189. package/src/utils/crc32/index.ts +83 -0
  190. package/src/utils/ensureDirectory/index.ts +10 -0
  191. package/src/utils/format/consts.ts +8 -0
  192. package/src/utils/format/index.ts +53 -0
  193. package/src/utils/getErrorMessage/index.ts +10 -0
  194. package/src/utils/index.ts +113 -0
  195. package/src/utils/ini/index.ts +200 -0
  196. package/src/utils/kitty/consts.ts +13 -0
  197. package/src/utils/kitty/index.ts +181 -0
  198. package/src/utils/logger/consts.ts +35 -0
  199. package/src/utils/logger/index.ts +217 -0
  200. package/src/utils/paths/consts.ts +18 -0
  201. package/src/utils/paths/index.ts +151 -0
  202. package/src/utils/png/consts.ts +34 -0
  203. package/src/utils/png/index.ts +131 -0
  204. package/src/utils/readJsonFile/index.ts +16 -0
  205. package/src/utils/rotateLogFile/index.ts +44 -0
  206. package/src/utils/safeClose/index.ts +10 -0
  207. package/src/utils/terminal/consts.ts +8 -0
  208. package/src/utils/terminal/index.ts +102 -0
  209. package/src/utils/thumbnailRenderer/consts.ts +2 -0
  210. package/src/utils/thumbnailRenderer/index.ts +147 -0
  211. package/src/utils/typedError/index.ts +26 -0
  212. package/tsconfig.json +31 -0
  213. package/vitest.config.ts +13 -0
@@ -0,0 +1,666 @@
1
+ # Save State Format (.state)
2
+
3
+ emoemu save states capture the complete emulator state, allowing games to be resumed exactly where they were left off. All cores use raw binary save states compatible with RetroArch.
4
+
5
+ ## Format
6
+
7
+ ### File Format
8
+
9
+ - **Naming**: `[rom name without extension].[coreId].state` (e.g., `game.libretro-fceumm.state`, `mario.libretro-bsnes.state`)
10
+ - **Format**: Raw binary (RetroArch-compatible)
11
+ - **Location**: Same directory as the ROM file
12
+
13
+ The naming convention includes the core ID because save states are not compatible across different cores. For example, playing `mario.sfc` with bsnes creates `mario.libretro-bsnes.state`, while playing with snes9x creates `mario.libretro-snes9x.state`.
14
+
15
+ ---
16
+
17
+ ## Game Boy Color Save States (.gbc.state / .gb.state)
18
+
19
+ GBC save states use `coreId: "gbc"` and `version: 1`.
20
+
21
+ ### GBC State Structure
22
+
23
+ ```json
24
+ {
25
+ "version": 1,
26
+ "coreId": "gbc",
27
+ "gameId": "zelda.gbc",
28
+ "frameCount": 12345,
29
+ "savedAt": "2025-01-15T12:00:00.000Z",
30
+ "data": {
31
+ "cpu": { ... },
32
+ "ppu": { ... },
33
+ "bus": { ... },
34
+ "timer": { ... },
35
+ "cartridge": { ... },
36
+ "cartridgeRam": [...],
37
+ "apu": { ... }
38
+ }
39
+ }
40
+ ```
41
+
42
+ ### GBC CPU State
43
+
44
+ The Game Boy uses a Sharp LR35902 CPU (Z80-like).
45
+
46
+ ```json
47
+ {
48
+ "a": 17,
49
+ "f": 128,
50
+ "b": 0,
51
+ "c": 19,
52
+ "d": 0,
53
+ "e": 216,
54
+ "h": 1,
55
+ "l": 77,
56
+ "sp": 65534,
57
+ "pc": 256,
58
+ "ime": false,
59
+ "halted": false,
60
+ "stopped": false,
61
+ "imeScheduled": false
62
+ }
63
+ ```
64
+
65
+ | Field | Type | Description |
66
+ |-------|------|-------------|
67
+ | `a` | number | Accumulator register (0-255) |
68
+ | `f` | number | Flags register (Z, N, H, C in upper nibble) |
69
+ | `b`, `c` | number | BC register pair (0-255 each) |
70
+ | `d`, `e` | number | DE register pair (0-255 each) |
71
+ | `h`, `l` | number | HL register pair (0-255 each) |
72
+ | `sp` | number | Stack pointer (0-65535) |
73
+ | `pc` | number | Program counter (0-65535) |
74
+ | `ime` | boolean | Interrupt Master Enable flag |
75
+ | `halted` | boolean | CPU halted (waiting for interrupt) |
76
+ | `stopped` | boolean | CPU stopped (for speed switch) |
77
+ | `imeScheduled` | boolean | IME to be enabled after next instruction (EI delay) |
78
+
79
+ ### GBC PPU State
80
+
81
+ ```json
82
+ {
83
+ "lcdc": 145,
84
+ "stat": 0,
85
+ "scy": 0,
86
+ "scx": 0,
87
+ "ly": 0,
88
+ "lyc": 0,
89
+ "wy": 0,
90
+ "wx": 0,
91
+ "bgp": 252,
92
+ "obp0": 255,
93
+ "obp1": 255,
94
+ "mode": 2,
95
+ "cycles": 0,
96
+ "windowLine": 0,
97
+ "frameComplete": false
98
+ }
99
+ ```
100
+
101
+ | Field | Type | Description |
102
+ |-------|------|-------------|
103
+ | `lcdc` | number | LCD Control register ($FF40) |
104
+ | `stat` | number | LCD Status register ($FF41) |
105
+ | `scy` | number | Scroll Y ($FF42) |
106
+ | `scx` | number | Scroll X ($FF43) |
107
+ | `ly` | number | Current scanline ($FF44, 0-153) |
108
+ | `lyc` | number | LY Compare ($FF45) |
109
+ | `wy` | number | Window Y position ($FF4A) |
110
+ | `wx` | number | Window X position ($FF4B) |
111
+ | `bgp` | number | Background palette (DMG mode, $FF47) |
112
+ | `obp0` | number | Object palette 0 (DMG mode, $FF48) |
113
+ | `obp1` | number | Object palette 1 (DMG mode, $FF49) |
114
+ | `mode` | number | PPU mode (0=HBlank, 1=VBlank, 2=OAM, 3=Drawing) |
115
+ | `cycles` | number | Cycles within current mode |
116
+ | `windowLine` | number | Internal window line counter |
117
+ | `frameComplete` | boolean | Frame completion flag |
118
+
119
+ ### GBC Bus State
120
+
121
+ ```json
122
+ {
123
+ "wram": [[...], [...], ...],
124
+ "hram": [...],
125
+ "oam": [...],
126
+ "vram": [[...], [...]],
127
+ "vramBank": 0,
128
+ "wramBank": 1,
129
+ "ie": 0,
130
+ "interruptFlags": 225,
131
+ "joypadState": 255,
132
+ "joypadSelect": 0,
133
+ "speedMode": 0,
134
+ "prepareSpeedSwitch": false,
135
+ "hdmaSource": 0,
136
+ "hdmaDest": 0,
137
+ "hdmaLength": 0,
138
+ "hdmaActive": false,
139
+ "bgPaletteIndex": 0,
140
+ "bgPaletteAutoInc": false,
141
+ "bgPaletteData": [...],
142
+ "objPaletteIndex": 0,
143
+ "objPaletteAutoInc": false,
144
+ "objPaletteData": [...]
145
+ }
146
+ ```
147
+
148
+ | Field | Type | Size | Description |
149
+ |-------|------|------|-------------|
150
+ | `wram` | number[][] | 8×4096 bytes | Work RAM (8 banks of 4KB) |
151
+ | `hram` | number[] | 127 bytes | High RAM ($FF80-$FFFE) |
152
+ | `oam` | number[] | 160 bytes | Object Attribute Memory |
153
+ | `vram` | number[][] | 2×8192 bytes | Video RAM (2 banks of 8KB) |
154
+ | `vramBank` | number | - | Current VRAM bank (0-1) |
155
+ | `wramBank` | number | - | Current WRAM bank (1-7) |
156
+ | `ie` | number | - | Interrupt Enable register ($FFFF) |
157
+ | `interruptFlags` | number | - | Interrupt Flags register ($FF0F) |
158
+ | `joypadState` | number | - | Joypad button state |
159
+ | `joypadSelect` | number | - | Joypad selection register |
160
+ | `speedMode` | number | - | GBC speed mode (0=normal, 1=double) |
161
+ | `prepareSpeedSwitch` | boolean | - | Speed switch pending |
162
+ | `hdmaSource` | number | - | HDMA source address |
163
+ | `hdmaDest` | number | - | HDMA destination address |
164
+ | `hdmaLength` | number | - | HDMA remaining length |
165
+ | `hdmaActive` | boolean | - | HDMA transfer active |
166
+ | `bgPaletteIndex` | number | - | Background palette index ($FF68) |
167
+ | `bgPaletteAutoInc` | boolean | - | Auto-increment on palette write |
168
+ | `bgPaletteData` | number[] | 64 bytes | Background palette data (8 palettes) |
169
+ | `objPaletteIndex` | number | - | Object palette index ($FF6A) |
170
+ | `objPaletteAutoInc` | boolean | - | Auto-increment on palette write |
171
+ | `objPaletteData` | number[] | 64 bytes | Object palette data (8 palettes) |
172
+
173
+ ### GBC Timer State
174
+
175
+ ```json
176
+ {
177
+ "div": 0,
178
+ "tima": 0,
179
+ "tma": 0,
180
+ "tac": 0,
181
+ "divCounter": 0,
182
+ "timaCounter": 0
183
+ }
184
+ ```
185
+
186
+ | Field | Type | Description |
187
+ |-------|------|-------------|
188
+ | `div` | number | Divider register ($FF04) |
189
+ | `tima` | number | Timer counter ($FF05) |
190
+ | `tma` | number | Timer modulo ($FF06) |
191
+ | `tac` | number | Timer control ($FF07) |
192
+ | `divCounter` | number | Internal divider counter |
193
+ | `timaCounter` | number | Internal timer counter |
194
+
195
+ ### GBC Cartridge State
196
+
197
+ ```json
198
+ {
199
+ "romBank": 1,
200
+ "ramBank": 0,
201
+ "ramEnabled": false,
202
+ "bankingMode": 0,
203
+ "rtcSelect": 0,
204
+ "rtcLatched": false,
205
+ "romBankHigh": 0
206
+ }
207
+ ```
208
+
209
+ | Field | Type | Description |
210
+ |-------|------|-------------|
211
+ | `romBank` | number | Current ROM bank (lower bits) |
212
+ | `ramBank` | number | Current RAM bank |
213
+ | `ramEnabled` | boolean | External RAM enabled |
214
+ | `bankingMode` | number | MBC1 banking mode (0=ROM, 1=RAM) |
215
+ | `rtcSelect` | number | MBC3 RTC register selected |
216
+ | `rtcLatched` | boolean | MBC3 RTC latched |
217
+ | `romBankHigh` | number | MBC5 ROM bank high bit |
218
+
219
+ The `cartridgeRam` field contains the full external RAM contents as a number array.
220
+
221
+ ### GBC APU State
222
+
223
+ The Game Boy APU has 4 channels with stereo panning.
224
+
225
+ ```json
226
+ {
227
+ "enabled": true,
228
+ "frameSequencer": 0,
229
+ "frameSequencerTimer": 8192,
230
+ "masterVolumeLeft": 7,
231
+ "masterVolumeRight": 7,
232
+ "panLeft": 15,
233
+ "panRight": 15,
234
+ "pulse1": { ... },
235
+ "pulse2": { ... },
236
+ "wave": { ... },
237
+ "noise": { ... }
238
+ }
239
+ ```
240
+
241
+ | Field | Type | Description |
242
+ |-------|------|-------------|
243
+ | `enabled` | boolean | APU master enable (NR52 bit 7) |
244
+ | `frameSequencer` | number | Frame sequencer step (0-7) |
245
+ | `frameSequencerTimer` | number | Cycles until next frame sequencer step |
246
+ | `masterVolumeLeft` | number | Master volume left (0-7) |
247
+ | `masterVolumeRight` | number | Master volume right (0-7) |
248
+ | `panLeft` | number | Channel panning to left (bits 0-3) |
249
+ | `panRight` | number | Channel panning to right (bits 0-3) |
250
+ | `pulse1` | object | Pulse channel 1 state (with sweep) |
251
+ | `pulse2` | object | Pulse channel 2 state |
252
+ | `wave` | object | Wave channel state |
253
+ | `noise` | object | Noise channel state |
254
+
255
+ #### GBC Pulse Channel State
256
+
257
+ ```json
258
+ {
259
+ "enabled": false,
260
+ "dacEnabled": false,
261
+ "lengthCounter": 0,
262
+ "lengthEnabled": false,
263
+ "frequency": 0,
264
+ "duty": 0,
265
+ "dutyPos": 0,
266
+ "timer": 0,
267
+ "volume": 0,
268
+ "volumeInitial": 0,
269
+ "volumeEnvDir": 0,
270
+ "volumeEnvPeriod": 0,
271
+ "volumeEnvTimer": 0,
272
+ "sweepEnabled": false,
273
+ "sweepPeriod": 0,
274
+ "sweepTimer": 0,
275
+ "sweepShift": 0,
276
+ "sweepNegate": false,
277
+ "sweepShadowFreq": 0,
278
+ "sweepCalcWithNegate": false
279
+ }
280
+ ```
281
+
282
+ #### GBC Wave Channel State
283
+
284
+ ```json
285
+ {
286
+ "enabled": false,
287
+ "dacEnabled": false,
288
+ "lengthCounter": 0,
289
+ "lengthEnabled": false,
290
+ "frequency": 0,
291
+ "timer": 0,
292
+ "volume": 0,
293
+ "position": 0,
294
+ "sampleBuffer": 0,
295
+ "waveRam": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
296
+ }
297
+ ```
298
+
299
+ #### GBC Noise Channel State
300
+
301
+ ```json
302
+ {
303
+ "enabled": false,
304
+ "dacEnabled": false,
305
+ "lengthCounter": 0,
306
+ "lengthEnabled": false,
307
+ "volume": 0,
308
+ "volumeInitial": 0,
309
+ "volumeEnvDir": 0,
310
+ "volumeEnvPeriod": 0,
311
+ "volumeEnvTimer": 0,
312
+ "divisor": 0,
313
+ "shift": 0,
314
+ "width": false,
315
+ "lfsr": 32767,
316
+ "timer": 0
317
+ }
318
+ ```
319
+
320
+ ---
321
+
322
+ ## SNES Save States (.sfc.state / .smc.state)
323
+
324
+ SNES save states use `coreId: "snes"` and `version: 1`.
325
+
326
+ ### SNES State Structure
327
+
328
+ ```json
329
+ {
330
+ "version": 1,
331
+ "coreId": "snes",
332
+ "gameId": "game.sfc",
333
+ "frameCount": 12345,
334
+ "savedAt": "2025-01-15T12:00:00.000Z",
335
+ "data": {
336
+ "cpu": { ... },
337
+ "ppu": { ... },
338
+ "apu": { ... },
339
+ "cartridge": { ... },
340
+ "ram": [...],
341
+ "dmaBadr": [...],
342
+ "dmaAadr": [...],
343
+ ...
344
+ }
345
+ }
346
+ ```
347
+
348
+ ### SNES CPU State (65C816)
349
+
350
+ ```json
351
+ {
352
+ "r": [0, 0],
353
+ "br": [0, 0, 0, 511, 0, 0],
354
+ "n": false,
355
+ "v": false,
356
+ "m": true,
357
+ "x": true,
358
+ "d": false,
359
+ "i": false,
360
+ "z": false,
361
+ "c": false,
362
+ "e": true,
363
+ "irqWanted": false,
364
+ "nmiWanted": false,
365
+ "stopped": false,
366
+ "waiting": false,
367
+ "cyclesLeft": 0
368
+ }
369
+ ```
370
+
371
+ | Field | Type | Description |
372
+ |-------|------|-------------|
373
+ | `r` | number[] | 8-bit registers: [DBR, PBR (program bank)] |
374
+ | `br` | number[] | 16-bit registers: [A, X, Y, SP, PC, DP] |
375
+ | `n` | boolean | Negative flag |
376
+ | `v` | boolean | Overflow flag |
377
+ | `m` | boolean | Memory/Accumulator size (1=8bit, 0=16bit) |
378
+ | `x` | boolean | Index register size (1=8bit, 0=16bit) |
379
+ | `d` | boolean | Decimal mode flag |
380
+ | `i` | boolean | Interrupt disable flag |
381
+ | `z` | boolean | Zero flag |
382
+ | `c` | boolean | Carry flag |
383
+ | `e` | boolean | Emulation mode flag |
384
+ | `irqWanted` | boolean | IRQ pending |
385
+ | `nmiWanted` | boolean | NMI pending |
386
+ | `stopped` | boolean | CPU stopped (STP instruction) |
387
+ | `waiting` | boolean | CPU waiting (WAI instruction) |
388
+ | `cyclesLeft` | number | Cycles remaining |
389
+
390
+ ### SNES PPU State
391
+
392
+ The SNES PPU state is extensive, covering all video registers and memory:
393
+
394
+ ```json
395
+ {
396
+ "vram": [...],
397
+ "cgram": [...],
398
+ "oam": [...],
399
+ "highOam": [...],
400
+ "cgramAdr": 0,
401
+ "cgramSecond": false,
402
+ "cgramBuffer": 0,
403
+ "vramInc": 1,
404
+ "vramRemap": 0,
405
+ "vramIncOnHigh": true,
406
+ "vramAdr": 0,
407
+ "vramReadBuffer": 0,
408
+ "tilemapWider": [false, false, false, false],
409
+ "tilemapHigher": [false, false, false, false],
410
+ "tilemapAdr": [0, 0, 0, 0],
411
+ "tileAdr": [0, 0, 0, 0],
412
+ "bgHoff": [0, 0, 0, 0],
413
+ "bgVoff": [0, 0, 0, 0],
414
+ "offPrev1": 0,
415
+ "offPrev2": 0,
416
+ "mode": 0,
417
+ "layer3Prio": false,
418
+ "bigTiles": [false, false, false, false],
419
+ "mosaicEnabled": [false, false, false, false],
420
+ "mosaicSize": 1,
421
+ "mosaicStartLine": 0,
422
+ "mainScreenEnabled": [false, false, false, false, false],
423
+ "subScreenEnabled": [false, false, false, false, false],
424
+ "forcedBlank": true,
425
+ "brightness": 0,
426
+ "oamAdr": 0,
427
+ ...
428
+ }
429
+ ```
430
+
431
+ | Field | Type | Size | Description |
432
+ |-------|------|------|-------------|
433
+ | `vram` | number[] | 65536 bytes | Video RAM |
434
+ | `cgram` | number[] | 512 bytes | Color palette RAM (256 colors × 2 bytes) |
435
+ | `oam` | number[] | 512 bytes | Object Attribute Memory (low table) |
436
+ | `highOam` | number[] | 32 bytes | OAM high table (size/position MSB) |
437
+ | `mode` | number | - | Background mode (0-7) |
438
+ | `brightness` | number | - | Master brightness (0-15) |
439
+ | `forcedBlank` | boolean | - | Forced blanking enabled |
440
+ | `tilemapAdr` | number[] | - | Tilemap addresses for BG1-4 |
441
+ | `tileAdr` | number[] | - | Tile data addresses for BG1-4 |
442
+ | `bgHoff` | number[] | - | Horizontal scroll for BG1-4 |
443
+ | `bgVoff` | number[] | - | Vertical scroll for BG1-4 |
444
+
445
+ ### SNES APU State
446
+
447
+ The SNES APU consists of the SPC700 CPU and S-DSP:
448
+
449
+ ```json
450
+ {
451
+ "ram": [...],
452
+ "spc": { ... },
453
+ "dsp": { ... },
454
+ "dspAddress": 0,
455
+ "cpuToPorts": [0, 0, 0, 0],
456
+ "portsToCpu": [0, 0, 0, 0],
457
+ "romReadable": true,
458
+ "timer0Target": 0,
459
+ "timer1Target": 0,
460
+ "timer2Target": 0,
461
+ "timer0Counter": 0,
462
+ "timer1Counter": 0,
463
+ "timer2Counter": 0,
464
+ "timer0Div": 0,
465
+ "timer1Div": 0,
466
+ "timer2Div": 0,
467
+ "timer0Enabled": false,
468
+ "timer1Enabled": false,
469
+ "timer2Enabled": false,
470
+ "dspCycleCounter": 0
471
+ }
472
+ ```
473
+
474
+ | Field | Type | Size | Description |
475
+ |-------|------|------|-------------|
476
+ | `ram` | number[] | 65536 bytes | Audio RAM (64KB) |
477
+ | `spc` | object | - | SPC700 CPU state |
478
+ | `dsp` | object | - | S-DSP state |
479
+ | `cpuToPorts` | number[] | 4 bytes | CPU → APU communication ports |
480
+ | `portsToCpu` | number[] | 4 bytes | APU → CPU communication ports |
481
+ | `romReadable` | boolean | - | IPL ROM readable flag |
482
+ | `timer*Target` | number | - | Timer target values |
483
+ | `timer*Counter` | number | - | Timer output counters |
484
+ | `timer*Enabled` | boolean | - | Timer enable flags |
485
+
486
+ #### SPC700 State
487
+
488
+ ```json
489
+ {
490
+ "r": [0, 0, 0, 239],
491
+ "br": [65472],
492
+ "n": false,
493
+ "v": false,
494
+ "p": false,
495
+ "b": false,
496
+ "h": false,
497
+ "i": false,
498
+ "z": false,
499
+ "c": false,
500
+ "cyclesLeft": 7
501
+ }
502
+ ```
503
+
504
+ | Field | Type | Description |
505
+ |-------|------|-------------|
506
+ | `r` | number[] | 8-bit registers: [A, X, Y, SP] |
507
+ | `br` | number[] | 16-bit registers: [PC] |
508
+ | `n` | boolean | Negative flag |
509
+ | `v` | boolean | Overflow flag |
510
+ | `p` | boolean | Direct page selector |
511
+ | `b` | boolean | Break flag |
512
+ | `h` | boolean | Half-carry flag |
513
+ | `i` | boolean | Interrupt enable |
514
+ | `z` | boolean | Zero flag |
515
+ | `c` | boolean | Carry flag |
516
+ | `cyclesLeft` | number | Cycles remaining |
517
+
518
+ #### S-DSP State
519
+
520
+ ```json
521
+ {
522
+ "ram": [...],
523
+ "decodeBuffer": [...],
524
+ "rateNums": [...],
525
+ "pitch": [...],
526
+ "counter": [...],
527
+ "pitchMod": [...],
528
+ "srcn": [...],
529
+ "decodeOffset": [...],
530
+ "prevFlags": [...],
531
+ "old": [...],
532
+ "older": [...],
533
+ "enableNoise": [...],
534
+ "noiseSample": 0,
535
+ "noiseRate": 0,
536
+ "noiseCounter": 0,
537
+ "rateCounter": [...],
538
+ "adsrState": [...],
539
+ "sustainLevel": [...],
540
+ "useGain": [...],
541
+ "gainMode": [...],
542
+ "directGain": [...],
543
+ "gainValue": [...],
544
+ "gain": [...],
545
+ "channelVolumeL": [...],
546
+ "channelVolumeR": [...],
547
+ "volumeL": 0,
548
+ "volumeR": 0,
549
+ "mute": false,
550
+ "resetFlag": false,
551
+ "noteOff": [...],
552
+ "sampleOut": [...],
553
+ "dirPage": 0
554
+ }
555
+ ```
556
+
557
+ | Field | Type | Description |
558
+ |-------|------|-------------|
559
+ | `ram` | number[] | DSP register RAM (128 bytes) |
560
+ | `pitch` | number[] | Voice pitch values (8 voices) |
561
+ | `adsrState` | number[] | ADSR state per voice (0-3) |
562
+ | `gain` | number[] | Current gain level per voice |
563
+ | `channelVolumeL` | number[] | Left volume per voice |
564
+ | `channelVolumeR` | number[] | Right volume per voice |
565
+ | `volumeL` | number | Master left volume |
566
+ | `volumeR` | number | Master right volume |
567
+ | `mute` | boolean | Master mute flag |
568
+ | `noiseSample` | number | Current noise sample value |
569
+ | `dirPage` | number | Sample directory page |
570
+
571
+ ### SNES Cartridge State
572
+
573
+ ```json
574
+ {
575
+ "sram": [...]
576
+ }
577
+ ```
578
+
579
+ | Field | Type | Size | Description |
580
+ |-------|------|------|-------------|
581
+ | `sram` | number[] | varies | Battery-backed SRAM (up to 128KB) |
582
+
583
+ ### SNES Main State Fields
584
+
585
+ Additional fields stored at the top level of `data`:
586
+
587
+ ```json
588
+ {
589
+ "ram": [...],
590
+ "dmaBadr": [...],
591
+ "dmaAadr": [...],
592
+ "dmaAadrBank": [...],
593
+ "dmaSize": [...],
594
+ "hdmaIndBank": [...],
595
+ "hdmaTableAdr": [...],
596
+ "hdmaRepCount": [...],
597
+ "dmaActive": [...],
598
+ "hdmaActive": [...],
599
+ "dmaMode": [...],
600
+ "dmaFixed": [...],
601
+ "dmaDec": [...],
602
+ "hdmaInd": [...],
603
+ "dmaFromB": [...],
604
+ "hdmaDoTransfer": [...],
605
+ "hdmaTerminated": [...],
606
+ "dmaBusy": false,
607
+ "dmaTimer": 0,
608
+ "hdmaTimer": 0,
609
+ "xPos": 0,
610
+ "yPos": 0,
611
+ "frames": 0,
612
+ "cpuCyclesLeft": 0,
613
+ "cpuMemOps": 0,
614
+ "apuCatchCycles": 0,
615
+ "ramAdr": 0,
616
+ "openBus": 0,
617
+ "fastMem": false,
618
+ "hIrqEnabled": false,
619
+ "vIrqEnabled": false,
620
+ "nmiEnabled": false,
621
+ "hTimer": 0,
622
+ "vTimer": 0,
623
+ "inNmi": false,
624
+ "inIrq": false,
625
+ "inHblank": false,
626
+ "inVblank": false,
627
+ "autoJoyRead": false,
628
+ "autoJoyTimer": 0,
629
+ "ppuLatch": false,
630
+ "joypad1Val": 0,
631
+ "joypad2Val": 0,
632
+ "joypad1AutoRead": 0,
633
+ "joypad2AutoRead": 0,
634
+ "joypadStrobe": false,
635
+ "joypad1State": 0,
636
+ "joypad2State": 0,
637
+ "multiplyA": 0,
638
+ "divA": 0,
639
+ "divResult": 0,
640
+ "mulResult": 0
641
+ }
642
+ ```
643
+
644
+ | Field | Type | Size | Description |
645
+ |-------|------|------|-------------|
646
+ | `ram` | number[] | 131072 bytes | Main RAM (128KB) |
647
+ | `dmaBadr` | number[] | 8 | DMA B-bus address per channel |
648
+ | `dmaAadr` | number[] | 8 | DMA A-bus address per channel |
649
+ | `dmaSize` | number[] | 8 | DMA transfer size per channel |
650
+ | `dmaActive` | boolean[] | 8 | DMA channel active flags |
651
+ | `hdmaActive` | boolean[] | 8 | HDMA channel active flags |
652
+ | `frames` | number | - | Frame counter |
653
+ | `xPos` | number | - | Current horizontal position (dot) |
654
+ | `yPos` | number | - | Current vertical position (scanline) |
655
+ | `hTimer` | number | - | H-IRQ timer value |
656
+ | `vTimer` | number | - | V-IRQ timer value |
657
+ | `inNmi` | boolean | - | In NMI handler |
658
+ | `inIrq` | boolean | - | In IRQ handler |
659
+ | `inHblank` | boolean | - | In horizontal blank |
660
+ | `inVblank` | boolean | - | In vertical blank |
661
+
662
+ ---
663
+
664
+ ## Version History
665
+
666
+ Save state formats are determined by each libretro core internally. State compatibility depends on the core version.