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
package/README.md ADDED
@@ -0,0 +1,404 @@
1
+ # 🎮 emoemu
2
+
3
+ <img width="100" alt="icon" src="https://github.com/user-attachments/assets/0447c490-0484-4b57-b35d-095ceaea7565" />
4
+
5
+ A terminal-based retro emulator written in TypeScript. Play classic games directly in your terminal using the Kitty graphics protocol, Unicode half-blocks, or ASCII characters. Supports **libretro cores** (RetroArch cores) to play NES, Game Boy, SNES, Sega Genesis, GBA, and more.
6
+
7
+ ## ✨ Features
8
+
9
+ ### General
10
+
11
+ - **Multiple render modes**: Native window (best performance), Kitty graphics protocol (best quality), Unicode half-blocks, ASCII art, or emoji
12
+ - **Post-processing modes**: Off, CRT preset, or Custom effects (scanlines, NTSC artifacts, bloom, vignette, gamma, etc.)
13
+ - **Diff-based rendering**: Optimized rendering that only updates changed pixels for better performance
14
+ - **Save states**: Automatic save/resume with gzip compression
15
+ - **Battery saves**: RetroArch-compatible `.srm` files for games with save RAM
16
+ - **Playlist generation**: Generate RetroArch-compatible `.lpl` playlists
17
+ - **Screenshots**: Capture screenshots with F8/F12, saved using RetroArch naming convention
18
+ - **Gamepad support**: Xbox (wired and wireless), PlayStation, Nintendo, and 8BitDo controllers via HID
19
+ - **Keyboard input**: Kitty keyboard protocol for accurate key detection, with legacy fallback
20
+ - **Dynamic scaling**: Auto-fits to terminal size with pixel aspect ratio correction
21
+ - **Multi-core architecture**: Extensible design for supporting additional systems
22
+ - **RetroArch integration**: Optionally load cores from existing RetroArch installation (`--retroarch`)
23
+ - **Netplay**: RetroArch-compatible multiplayer with rollback netcode
24
+
25
+ ### Supported Systems
26
+
27
+ | System | Status | Extensions |
28
+ | -------------- | ------------------ | --------------- |
29
+ | NES | ✅ Via libretro | `.nes` |
30
+ | Game Boy / Color | ✅ Via libretro | `.gb`, `.gbc` |
31
+ | SNES | ✅ Via libretro | `.sfc`, `.smc` |
32
+ | Sega Genesis | ✅ Via libretro | `.md`, `.gen` |
33
+ | Game Boy Advance | ✅ Via libretro | `.gba` |
34
+ | Nintendo 64 | ✅ Via libretro | `.n64`, `.z64`, `.v64` |
35
+ | + Many more | ✅ Via libretro | Various |
36
+
37
+ > **Note:** Libretro cores must be installed separately. See [Using Libretro Cores](#-using-libretro-cores) below.
38
+
39
+ ## 📋 Requirements
40
+
41
+ - Node.js 24+
42
+ - A terminal emulator (see below)
43
+
44
+ ### Terminal Emulators
45
+
46
+ You can use any terminal emulator, but for the best experience we recommend using a terminal that supports both:
47
+
48
+ - **[Kitty Graphics Protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/)** - Enables high-quality pixel-perfect rendering directly in the terminal
49
+ - **[Kitty Keyboard Protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/)** - Enables detecting multiple simultaneous key presses, which is required for many game actions (e.g., holding B to run while tapping A to jump)
50
+
51
+ #### ⭐ Recommended
52
+
53
+ These terminals support both high-quality graphics (Kitty graphics protocol) and the best keyboard experience (Kitty keyboard protocol):
54
+
55
+ - [Ghostty](https://ghostty.org/)
56
+ - [Kitty](https://sw.kovidgoyal.net/kitty/)
57
+
58
+ #### Other Terminals
59
+
60
+ Any other terminal will work with `--terminal` or `--ascii` mode. A gamepad is recommended since standard terminal input cannot detect multiple simultaneous key presses.
61
+
62
+ ## 🚀 Quick Start
63
+
64
+ ```bash
65
+ npx emoemu /path/to/game.nes
66
+ ```
67
+
68
+ ## 📥 Installation
69
+
70
+ ```bash
71
+ # Install globally
72
+ npm install -g emoemu
73
+
74
+ # Run
75
+ emoemu /path/to/game.nes
76
+ ```
77
+
78
+ ## 📖 Usage
79
+
80
+ ```bash
81
+ emoemu <rom> [options]
82
+ ```
83
+
84
+ ### Core Selection
85
+
86
+ ```bash
87
+ emoemu game.nes # Auto-detect core by file extension
88
+ emoemu game.nes --core fceumm # Explicitly select a core
89
+ emoemu --list-cores # Show available cores
90
+ emoemu --retroarch --list-cores # Include RetroArch cores
91
+ ```
92
+
93
+ When multiple cores support the same ROM extension (e.g., `.sfc` works with both bsnes and snes9x), you'll be prompted to select which core to use. Use `--core <id>` to skip the prompt.
94
+
95
+ Use `--retroarch` to load libretro cores from RetroArch installation directories (see [Using Libretro Cores](#-using-libretro-cores)).
96
+
97
+ ### Render Modes
98
+
99
+ ```bash
100
+ emoemu game.nes --native # Native window (best performance, zero dependencies)
101
+ emoemu game.nes # Kitty graphics (default for most systems)
102
+ emoemu game.nes --terminal # Unicode half-blocks (default for N64)
103
+ emoemu game.nes --ascii # ASCII characters
104
+ emoemu game.nes --emoji # Emoji characters
105
+ ```
106
+
107
+ **Render Mode:** By default, render mode is set to "Auto" which uses system-appropriate defaults: Kitty graphics for most systems (best quality) and Terminal mode for N64 (better performance with software rendering). You can override this in settings or with `--terminal`, `--kitty`, `--native`, etc.
108
+
109
+ > **Note:** The `--native` mode renders to a native window instead of the terminal, bypassing terminal I/O for the best performance. The window backend (fenster) and bitmap font are bundled — **no system dependencies to install**.
110
+
111
+ > **Tip:** When using `--terminal`, `--ascii`, or `--emoji` modes, scale your terminal font down to a small size so the characters are small enough to render graphics clearly. For example, on Ghostty or Kitty on macOS, press `Cmd+-` repeatedly. The default Kitty graphics mode renders at pixel level and doesn't require this.
112
+
113
+ ### Display Options
114
+
115
+ ```bash
116
+ emoemu game.nes --scale 2 # Fixed scale (Kitty mode, 0.25x-4x)
117
+ emoemu game.nes --png-level 1 # PNG compression 1-9 for Kitty mode (default: 4)
118
+ emoemu game.nes --width 120 # Fixed width (terminal/ASCII)
119
+ emoemu game.nes --height 40 # Fixed height (terminal/ASCII)
120
+ emoemu game.nes --no-color # Disable colors (terminal/ASCII modes)
121
+ emoemu game.nes --no-diff-render # Disable diff-based rendering optimization
122
+ emoemu game.nes --status # Show the status bar
123
+ ```
124
+
125
+ **Video Scale:** By default, video scale is set to "Auto" which uses system-appropriate defaults: 2x for most systems (NES, SNES, Genesis, etc.) and 0.5x for N64. You can override this in settings or with `--scale`.
126
+
127
+ **Post-Processing Modes:**
128
+
129
+ Post-processing can be configured via the settings panel or toggled with the P key during gameplay. Three modes are available:
130
+
131
+ - **Off**: No effects applied (clean pixels)
132
+ - **CRT**: Preset values for an authentic retro look (gamma 1.3, scanlines 0.1, vignette 0.5, NTSC 1.0, curvature 0.1)
133
+ - **Custom**: User-defined effect values from config or command line
134
+
135
+ The P key cycles through: Off → Custom (if defined) → CRT → Off
136
+
137
+ **Custom Effect Settings:**
138
+
139
+ ```bash
140
+ emoemu game.nes --gamma 1.2 # Gamma correction (default: 1.0)
141
+ emoemu game.nes --scanlines 0.3 # Scanline intensity (default: 0)
142
+ emoemu game.nes --saturation 1.2 # Color saturation (default: 1.0)
143
+ emoemu game.nes --brightness 1.1 # Brightness multiplier (default: 1.0)
144
+ emoemu game.nes --contrast 1.1 # Contrast adjustment (default: 1.0)
145
+ emoemu game.nes --vignette 0.5 # Vignette edge darkening (default: 0)
146
+ ```
147
+
148
+ - `--gamma` adjusts display gamma. Values above 1.0 darken midtones for richer colors. Try `1.1` to `1.4`.
149
+ - `--scanlines` adds horizontal scanline darkening to simulate CRT phosphor gaps. Try `0.2` to `0.4` for subtle effect.
150
+ - `--saturation` boosts color vibrancy. Values above 1.0 increase saturation. Try `1.1` to `1.3` for CRT-like colors.
151
+ - `--brightness` adjusts overall brightness. Values above 1.0 brighten, below 1.0 darken.
152
+ - `--contrast` adjusts tonal range. Values above 1.0 increase contrast, below 1.0 flatten.
153
+ - `--vignette` darkens screen edges to simulate CRT electron beam falloff. Try `0.3` to `0.5`.
154
+
155
+ **Additional Effects (Kitty mode only):**
156
+
157
+ ```bash
158
+ emoemu game.nes --crt # Start with CRT mode enabled
159
+ ```
160
+
161
+ - `--crt` starts the emulator with CRT post-processing mode selected. Use P key or settings to switch to Custom mode if you want to use custom effect values.
162
+ - `--bloom` adds phosphor glow around bright areas. Try `0.3` to `0.6` for subtle CRT glow.
163
+ - `--bloom-threshold` sets brightness threshold for bloom effect. Default `0.6` (range 0-1).
164
+ - `--ntsc` simulates horizontal color bleeding from composite video signals. Try `0.5` to `1.0`.
165
+ - `--curvature` applies barrel distortion to simulate curved CRT glass. Try `0.1` to `0.3`.
166
+
167
+ ### Emulation
168
+
169
+ ```bash
170
+ emoemu game.nes --fps-limit 30 # Override FPS limit (0 = uncapped)
171
+ ```
172
+
173
+ ### Audio
174
+
175
+ ```bash
176
+ emoemu game.nes # Audio enabled (default)
177
+ emoemu game.nes --no-audio # Disable audio
178
+ ```
179
+
180
+ ### Gamepad
181
+
182
+ ```bash
183
+ emoemu --list-gamepads # Show detected controllers
184
+ emoemu game.nes --debug-gamepad # Debug raw HID data
185
+ emoemu game.nes --no-gamepad # Disable gamepad support
186
+ ```
187
+
188
+ ### Save Data
189
+
190
+ ```bash
191
+ emoemu game.nes # Auto-save state and battery saves (default)
192
+ emoemu game.nes --no-save-state # Disable save state loading/saving
193
+ emoemu game.nes --no-battery-save # Disable battery save (.srm) loading/saving
194
+ emoemu game.nes --no-gzip-state # Save states uncompressed (for debugging)
195
+ ```
196
+
197
+ Save states are saved automatically on exit. File names include the core ID (e.g., `game.libretro-fceumm.state`, `game.libretro-bsnes.state`) to prevent conflicts when using different cores for the same ROM.
198
+
199
+ Battery saves (`.srm` files) store cartridge SRAM for games with battery-backed saves (e.g., Zelda, Pokemon) and are RetroArch-compatible.
200
+
201
+ ### Playlist Generation
202
+
203
+ Generate RetroArch-compatible `.lpl` playlist files from your ROM collection:
204
+
205
+ ```bash
206
+ emoemu --generate-playlist /path/to/roms # Generate per-system playlists
207
+ emoemu --generate-playlist /path/to/roms --single-playlist "My Games" # Single playlist
208
+ emoemu --retroarch --generate-playlist /path/to/roms # Include RetroArch cores
209
+ ```
210
+
211
+ | Option | Description |
212
+ |--------|-------------|
213
+ | `--generate-playlist [path]` | Scan directory for ROMs (default: current directory) |
214
+ | `--playlist-output <dir>` | Output directory (default: platform-specific, e.g., `~/.config/emoemu/playlists/`) |
215
+ | `--single-playlist <name>` | Create one playlist instead of per-system |
216
+ | `--windows-paths` | Use Windows backslash separators |
217
+
218
+ ### Netplay
219
+
220
+ Play multiplayer games over the network using RetroArch-compatible netplay:
221
+
222
+ ```bash
223
+ # Host a session (player 1)
224
+ emoemu game.sfc --netplay-host --netplay-nick "Player1"
225
+
226
+ # Join via LAN discovery (auto-finds host on local network)
227
+ emoemu game.sfc --netplay-connect --netplay-nick "Player2"
228
+
229
+ # Join a specific host (player 2)
230
+ emoemu game.sfc --netplay-connect 192.168.1.100 --netplay-nick "Player2"
231
+
232
+ # Join with password
233
+ emoemu game.sfc --netplay-connect host:55435 --netplay-password secret
234
+
235
+ # Spectate a session
236
+ emoemu game.sfc --netplay-connect 192.168.1.100 --netplay-spectate
237
+ ```
238
+
239
+ | Option | Description |
240
+ |--------|-------------|
241
+ | `--netplay-host` | Host a netplay session (server mode) |
242
+ | `--netplay-connect [host]` | Connect to server (host or host:port). Omit host for LAN auto-discovery |
243
+ | `--netplay-port <n>` | Port for netplay (default: 55435) |
244
+ | `--netplay-password <pw>` | Password for the session |
245
+ | `--netplay-spectate` | Join as spectator (view only) |
246
+ | `--netplay-nick <name>` | Your nickname (default: Player) |
247
+ | `--netplay-frames <n>` | Input delay 0-16 (higher = fewer rollbacks, more latency) |
248
+
249
+ > **Note:** Netplay requires libretro cores.
250
+
251
+ ## 🔌 Using Libretro Cores
252
+
253
+ emoemu can load native [libretro cores](https://www.libretro.com/) (the same cores used by RetroArch) to support additional systems without any configuration.
254
+
255
+ ### Using Cores from RetroArch
256
+
257
+ If you already have RetroArch installed with cores, use the `--retroarch` flag to load them:
258
+
259
+ ```bash
260
+ emoemu game.md --retroarch # Load RetroArch cores and play
261
+ emoemu --retroarch --list-cores # Show all cores including RetroArch
262
+ ```
263
+
264
+ This reads your `retroarch.cfg` (read-only) to find the core directory.
265
+
266
+ ### Installing Cores
267
+
268
+ #### Core Manager (Recommended)
269
+
270
+ Use the built-in Core Manager to download and manage cores:
271
+
272
+ 1. Launch emoemu and open the ROM browser
273
+ 2. Tab to the action bar and select **Manage Cores**
274
+ 3. Switch to the **Download** tab to see recommended cores
275
+ 4. Press Enter to download a core
276
+
277
+ The Core Manager shows recommended cores for popular systems and provides access to the full libretro buildbot catalog.
278
+
279
+ #### Manual Install
280
+
281
+ 1. **Download cores** from the [RetroArch buildbot](https://buildbot.libretro.com/nightly/):
282
+ - macOS: Choose `apple/osx/arm64` or `apple/osx/x86_64`
283
+ - Linux: Choose `linux/x86_64`
284
+ - Windows: Choose `windows/x86_64`
285
+
286
+ 2. **Place cores** in one of these directories:
287
+ | Platform | Directory |
288
+ |----------|-----------|
289
+ | macOS | `~/Library/Application Support/emoemu/cores/` |
290
+ | Linux | `~/.config/emoemu/cores/` |
291
+ | Windows | `%APPDATA%\emoemu\cores\` |
292
+
293
+ 3. **Run your game** - cores are auto-detected by ROM extension:
294
+ ```bash
295
+ emoemu game.md # Auto-detects picodrive
296
+ emoemu game.gba # Auto-detects mgba
297
+ emoemu --list-cores # Show all available cores
298
+ ```
299
+
300
+ ### Recommended Cores
301
+
302
+ | Core | Systems |
303
+ |------|---------|
304
+ | `bsnes` | SNES |
305
+ | `mgba` | Game Boy Advance |
306
+ | `gambatte` | Game Boy / Color |
307
+ | `picodrive` | Sega Genesis / Mega Drive |
308
+ | `mupen64plus_next` | Nintendo 64 |
309
+
310
+ ### BIOS Files
311
+
312
+ Some cores require BIOS files. Place them in `./system/`:
313
+ ```
314
+ ./system/
315
+ ├── gba_bios.bin # GBA BIOS (optional for mgba)
316
+ ├── bios_CD_U.bin # Sega CD BIOS (for picodrive)
317
+ └── syscard3.pce # PC Engine CD BIOS
318
+ ```
319
+
320
+ ### Limitations
321
+
322
+ - **Software rendering only**: Cores requiring OpenGL/Vulkan won't work (e.g., PPSSPP, Dolphin)
323
+ - **N64 on macOS ARM64**: Requires Xcode CLI tools (`xcode-select --install`). The core is automatically built from source with software rendering since pre-built ARM64 binaries require OpenGL.
324
+ - **No core options UI**: Core-specific settings use defaults
325
+
326
+ ## 🎮 Controls
327
+
328
+ ### Keyboard
329
+
330
+ | Button | Primary | Alternate |
331
+ | ------ | ------- | ---------- |
332
+ | D-Pad | WASD | Arrow keys |
333
+ | A | K | Z |
334
+ | B | J | X |
335
+ | Start | Enter | |
336
+ | Select | Space | |
337
+
338
+ ### Shortcuts
339
+
340
+ | Key | Action |
341
+ | ------ | ----------------------------- |
342
+ | F8/F12 | Take screenshot |
343
+ | M | Toggle audio mute |
344
+ | P | Cycle post-processing (Off → Custom → CRT) |
345
+ | R | Cycle render mode |
346
+ | Esc | Quit |
347
+ | Ctrl+C | Quit |
348
+
349
+ ### Gamepad
350
+
351
+ Standard gamepad mapping is used. Controllers are auto-detected with hotplug support.
352
+
353
+ Supported controllers:
354
+
355
+ - Xbox (360, One, Series X/S)
356
+ - PlayStation (DS4, DualSense)
357
+ - Nintendo (Switch Pro, Joy-Cons)
358
+ - 8BitDo controllers
359
+
360
+ **Analog stick support**: For systems that require analog input (like N64), the left analog stick is mapped directly to the core's analog axes. This enables proper control in games like Super Mario 64 that need precise analog movement.
361
+
362
+ ## 🔨 Building from Source
363
+
364
+ ```bash
365
+ git clone https://github.com/tuxracer/emoemu.git
366
+ cd emoemu
367
+ pnpm install # Install dependencies
368
+ pnpm run build # Build the project
369
+ pnpm run typecheck # Type check without building
370
+ pnpm run test:run # Run tests
371
+ pnpm run check # Run typecheck, lint, and tests
372
+ pnpm run start /path/to/game.nes
373
+ ```
374
+
375
+ ## Project Structure
376
+
377
+ ```
378
+ src/
379
+ ├── index.ts # CLI entry point
380
+ ├── cli/ # Argument parsing, commands, emulator runner
381
+ ├── Emulator/ # Main emulation loop, renderer orchestration
382
+ ├── core/ # Multi-core interface definitions
383
+ ├── frontend/ # Shared frontend (audio, state, registry)
384
+ ├── cores/
385
+ │ └── libretro/ # Libretro core wrapper (RetroArch cores)
386
+ ├── input/ # Keyboard and gamepad input
387
+ ├── rendering/ # Terminal renderers
388
+ ├── netplay/ # RetroArch-compatible netplay (rollback, LAN discovery)
389
+ └── ui/ # React/Ink TUI (ROM browser, settings)
390
+ ```
391
+
392
+ ## Architecture
393
+
394
+ emoemu uses a **libretro-inspired** multi-core architecture:
395
+
396
+ - **Cores** (`src/cores/`): System-specific emulation (CPU, PPU, APU, memory)
397
+ - **Frontend** (`src/frontend/`): Shared infrastructure (audio, input, rendering, state)
398
+ - **Core Interface** (`src/core/`): Standard interface all cores implement
399
+
400
+ This separation allows adding new systems without modifying shared code. See [docs/cores-trd.md](docs/cores-trd.md) for the full architecture documentation.
401
+
402
+ ## 📄 License
403
+
404
+ MIT