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.
- package/.claude/settings.local.json +77 -0
- package/.node-version +1 -0
- package/CLAUDE.md +435 -0
- package/README.md +404 -0
- package/TODO.md +655 -0
- package/dist/index.cjs +25108 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +25085 -0
- package/docs/building-libretro-cores-arm-mac.md +237 -0
- package/docs/config-file-format.md +488 -0
- package/docs/cores-trd.md +425 -0
- package/docs/headless-hardware-rendering-trd.md +676 -0
- package/docs/libretro-cores-trd.md +997 -0
- package/docs/mupen64-software-rendering-trd.md +751 -0
- package/docs/n64-support-trd.md +306 -0
- package/docs/native-rendering-trd.md +540 -0
- package/docs/native-ui-rendering-trd.md +1195 -0
- package/docs/netplay-trd.md +665 -0
- package/docs/retroarch-netplay-docs.md +277 -0
- package/docs/save-state-format.md +666 -0
- package/eslint.config.js +111 -0
- package/icon/icon.png +0 -0
- package/icon/icon.pxd +0 -0
- package/package.json +63 -0
- package/pnpm-workspace.yaml +10 -0
- package/src/Emulator/consts.ts +14 -0
- package/src/Emulator/index.ts +2496 -0
- package/src/Emulator/saveState/index.ts +155 -0
- package/src/Emulator/screenshot/index.ts +160 -0
- package/src/Emulator/terminalDimensions/index.ts +79 -0
- package/src/Emulator/types.ts +83 -0
- package/src/cli/commands/consts.ts +10 -0
- package/src/cli/commands/index.ts +462 -0
- package/src/cli/parseArgs/consts.ts +17 -0
- package/src/cli/parseArgs/index.ts +457 -0
- package/src/cli/parseArgs/types.ts +61 -0
- package/src/cli/runEmulator/index.ts +406 -0
- package/src/cli/runEmulator/types.ts +7 -0
- package/src/consts.ts +19 -0
- package/src/core/button/consts.ts +35 -0
- package/src/core/button/index.ts +123 -0
- package/src/core/core.ts +300 -0
- package/src/core/index.ts +19 -0
- package/src/cores/libretro/api/index.ts +334 -0
- package/src/cores/libretro/api/types.ts +148 -0
- package/src/cores/libretro/callbacks/consts.ts +41 -0
- package/src/cores/libretro/callbacks/index.ts +456 -0
- package/src/cores/libretro/consts.ts +45 -0
- package/src/cores/libretro/coreOptions/consts.ts +36 -0
- package/src/cores/libretro/coreOptions/index.ts +222 -0
- package/src/cores/libretro/environment/consts.ts +118 -0
- package/src/cores/libretro/environment/index.ts +1095 -0
- package/src/cores/libretro/index.ts +937 -0
- package/src/cores/libretro/loader/index.ts +496 -0
- package/src/cores/libretro/pixelFormat/consts.ts +43 -0
- package/src/cores/libretro/pixelFormat/index.ts +397 -0
- package/src/cores/libretro/types.ts +339 -0
- package/src/frontend/AudioManager/index.ts +420 -0
- package/src/frontend/SettingsManager/index.ts +250 -0
- package/src/frontend/config/index.ts +608 -0
- package/src/frontend/config/tests.ts +354 -0
- package/src/frontend/config/types.ts +36 -0
- package/src/frontend/consts.ts +114 -0
- package/src/frontend/coreBuilder/index.ts +644 -0
- package/src/frontend/coreBuilder/types.ts +15 -0
- package/src/frontend/coreDownloader/index.ts +620 -0
- package/src/frontend/coreDownloader/types.ts +17 -0
- package/src/frontend/corePreferences/index.ts +69 -0
- package/src/frontend/coreRegistry/index.ts +276 -0
- package/src/frontend/directoryCache/index.ts +75 -0
- package/src/frontend/index.ts +79 -0
- package/src/frontend/notifications/consts.ts +14 -0
- package/src/frontend/notifications/index.ts +250 -0
- package/src/frontend/playlist/consts.ts +168 -0
- package/src/frontend/playlist/index.ts +899 -0
- package/src/frontend/playlist/labelFormatter/consts.ts +15 -0
- package/src/frontend/playlist/labelFormatter/index.ts +155 -0
- package/src/frontend/playlist/labelFormatter/tests.ts +153 -0
- package/src/frontend/playlist/reader/index.ts +559 -0
- package/src/frontend/playlist/sync/index.ts +511 -0
- package/src/frontend/playlist/systemLookup/index.ts +233 -0
- package/src/frontend/playlist/utils/index.ts +50 -0
- package/src/frontend/romScanner/consts.ts +348 -0
- package/src/frontend/romScanner/index.ts +1957 -0
- package/src/frontend/saveServices/consts.ts +2 -0
- package/src/frontend/saveServices/index.ts +313 -0
- package/src/frontend/serviceProvider/index.ts +108 -0
- package/src/frontend/serviceProvider/types.ts +13 -0
- package/src/index.ts +428 -0
- package/src/input/Controller/consts.ts +50 -0
- package/src/input/Controller/index.ts +81 -0
- package/src/input/GamepadManager/consts.ts +22 -0
- package/src/input/GamepadManager/index.ts +418 -0
- package/src/input/InputManager/consts.ts +86 -0
- package/src/input/InputManager/index.ts +593 -0
- package/src/input/InputMapper/consts.ts +33 -0
- package/src/input/InputMapper/index.ts +436 -0
- package/src/input/consts.ts +410 -0
- package/src/input/gamepadProfiles/index.ts +1100 -0
- package/src/input/index.ts +38 -0
- package/src/input/inputUtils/index.ts +31 -0
- package/src/netplay/FrameBuffer/consts.ts +2 -0
- package/src/netplay/FrameBuffer/index.ts +364 -0
- package/src/netplay/FrameBuffer/tests.ts +286 -0
- package/src/netplay/InputBuffer/consts.ts +7 -0
- package/src/netplay/InputBuffer/index.ts +347 -0
- package/src/netplay/InputBuffer/tests.ts +166 -0
- package/src/netplay/NetplayClient/index.ts +976 -0
- package/src/netplay/NetplayConnection/index.ts +536 -0
- package/src/netplay/NetplayDiscovery/consts.ts +41 -0
- package/src/netplay/NetplayDiscovery/index.ts +525 -0
- package/src/netplay/NetplayServer/index.ts +1407 -0
- package/src/netplay/SyncManager/index.ts +984 -0
- package/src/netplay/SyncManager/tests.ts +419 -0
- package/src/netplay/consts.ts +371 -0
- package/src/netplay/crc32/consts.ts +14 -0
- package/src/netplay/crc32/index.ts +97 -0
- package/src/netplay/crc32/tests.ts +40 -0
- package/src/netplay/index.ts +41 -0
- package/src/netplay/netplayLogger/consts.ts +30 -0
- package/src/netplay/netplayLogger/index.ts +345 -0
- package/src/netplay/protocol/consts.ts +86 -0
- package/src/netplay/protocol/index.ts +1280 -0
- package/src/netplay/protocol/tests.ts +606 -0
- package/src/netplay/protocol/types.ts +20 -0
- package/src/netplay/types.ts +395 -0
- package/src/rendering/KittyRenderer/index.ts +616 -0
- package/src/rendering/NativeRenderer/index.ts +279 -0
- package/src/rendering/NativeRenderer/tests.ts +133 -0
- package/src/rendering/TerminalRenderer/index.ts +770 -0
- package/src/rendering/consts.ts +401 -0
- package/src/rendering/fonts/CozetteVector.ttf +0 -0
- package/src/rendering/index.ts +26 -0
- package/src/rendering/nativeUi/NativeWindowManager/index.ts +158 -0
- package/src/rendering/nativeUi/NativeWindowManager/tests.ts +81 -0
- package/src/rendering/nativeUi/consts.ts +6 -0
- package/src/rendering/nativeUi/index.ts +20 -0
- package/src/rendering/postProcessing/consts.ts +38 -0
- package/src/rendering/postProcessing/index.ts +923 -0
- package/src/rendering/shared/ansi/consts.ts +12 -0
- package/src/rendering/shared/ansi/index.ts +104 -0
- package/src/rendering/shared/consts.ts +2 -0
- package/src/rendering/shared/fitToTerminal/index.ts +67 -0
- package/src/ui/AddRomsPrompt/consts.ts +13 -0
- package/src/ui/AddRomsPrompt/index.tsx +781 -0
- package/src/ui/App/consts.ts +2 -0
- package/src/ui/App/index.tsx +456 -0
- package/src/ui/AppCapabilities/index.tsx +67 -0
- package/src/ui/ConfigContext/index.tsx +56 -0
- package/src/ui/CoreManager/consts.ts +11 -0
- package/src/ui/CoreManager/index.tsx +779 -0
- package/src/ui/CoreSelector/consts.ts +2 -0
- package/src/ui/CoreSelector/index.tsx +251 -0
- package/src/ui/DialogContainer/index.tsx +42 -0
- package/src/ui/DialogOptionsList/index.tsx +61 -0
- package/src/ui/DuplicateCrcPrompt/consts.ts +5 -0
- package/src/ui/DuplicateCrcPrompt/index.tsx +146 -0
- package/src/ui/GamepadContext/consts.ts +15 -0
- package/src/ui/GamepadContext/index.tsx +295 -0
- package/src/ui/NativeDialog/index.tsx +120 -0
- package/src/ui/NetplayDisconnectedDialog/index.tsx +93 -0
- package/src/ui/NetplayPauseMenu/consts.ts +2 -0
- package/src/ui/NetplayPauseMenu/index.tsx +97 -0
- package/src/ui/RomBrowser/NetplayPanel/consts.ts +24 -0
- package/src/ui/RomBrowser/NetplayPanel/index.tsx +520 -0
- package/src/ui/RomBrowser/SettingsPanel/index.tsx +478 -0
- package/src/ui/RomBrowser/consts.ts +61 -0
- package/src/ui/RomBrowser/index.tsx +1164 -0
- package/src/ui/RomBrowser/settingsConfig/index.ts +320 -0
- package/src/ui/RomBrowser/types.ts +67 -0
- package/src/ui/SaveStateDialog/consts.ts +2 -0
- package/src/ui/SaveStateDialog/index.tsx +225 -0
- package/src/ui/WarningDialog/index.tsx +113 -0
- package/src/ui/consts.ts +27 -0
- package/src/ui/hooks/useClearTerminal/consts.ts +2 -0
- package/src/ui/hooks/useClearTerminal/index.ts +37 -0
- package/src/ui/hooks/useDialogNavigation/index.ts +99 -0
- package/src/ui/hooks/useGamepad/consts.ts +21 -0
- package/src/ui/hooks/useGamepad/index.ts +194 -0
- package/src/ui/index.ts +27 -0
- package/src/utils/buffer/consts.ts +17 -0
- package/src/utils/buffer/index.ts +129 -0
- package/src/utils/color/consts.ts +58 -0
- package/src/utils/color/index.ts +183 -0
- package/src/utils/compression/consts.ts +50 -0
- package/src/utils/compression/index.ts +101 -0
- package/src/utils/consts.ts +2 -0
- package/src/utils/crc32/consts.ts +22 -0
- package/src/utils/crc32/index.ts +83 -0
- package/src/utils/ensureDirectory/index.ts +10 -0
- package/src/utils/format/consts.ts +8 -0
- package/src/utils/format/index.ts +53 -0
- package/src/utils/getErrorMessage/index.ts +10 -0
- package/src/utils/index.ts +113 -0
- package/src/utils/ini/index.ts +200 -0
- package/src/utils/kitty/consts.ts +13 -0
- package/src/utils/kitty/index.ts +181 -0
- package/src/utils/logger/consts.ts +35 -0
- package/src/utils/logger/index.ts +217 -0
- package/src/utils/paths/consts.ts +18 -0
- package/src/utils/paths/index.ts +151 -0
- package/src/utils/png/consts.ts +34 -0
- package/src/utils/png/index.ts +131 -0
- package/src/utils/readJsonFile/index.ts +16 -0
- package/src/utils/rotateLogFile/index.ts +44 -0
- package/src/utils/safeClose/index.ts +10 -0
- package/src/utils/terminal/consts.ts +8 -0
- package/src/utils/terminal/index.ts +102 -0
- package/src/utils/thumbnailRenderer/consts.ts +2 -0
- package/src/utils/thumbnailRenderer/index.ts +147 -0
- package/src/utils/typedError/index.ts +26 -0
- package/tsconfig.json +31 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default core options for emoemu compatibility.
|
|
3
|
+
*
|
|
4
|
+
* Since emoemu is terminal-based and doesn't have GPU support, some cores
|
|
5
|
+
* need specific options to work (e.g., software rendering for N64).
|
|
6
|
+
* These defaults are applied automatically when creating a core.
|
|
7
|
+
*
|
|
8
|
+
* Keys are matched against the core's library name (case-insensitive).
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_CORE_OPTIONS: Record<string, Record<string, string>> = {
|
|
11
|
+
// N64: Use Angrylion software renderer (no GPU required)
|
|
12
|
+
// Also enable auto-crop to remove black borders many N64 games add
|
|
13
|
+
'mupen64plus': {
|
|
14
|
+
'mupen64plus-rdp-plugin': 'angrylion',
|
|
15
|
+
'mupen64plus-rsp-plugin': 'cxd4', // Required for Angrylion (HLE RSP won't work)
|
|
16
|
+
'mupen64plus-cpucore': 'cached_interpreter', // ARM64 doesn't have dynarec
|
|
17
|
+
'mupen64plus-angrylion-multithread': 'all threads',
|
|
18
|
+
'mupen64plus-aspect': '4:3',
|
|
19
|
+
'mupen64plus-43screensize': '640x480',
|
|
20
|
+
'mupen64plus-CropMode': 'Auto',
|
|
21
|
+
// Angrylion VI settings (VI filter can cause black screen)
|
|
22
|
+
'mupen64plus-angrylion-vioverlay': 'Filtered',
|
|
23
|
+
'mupen64plus-angrylion-sync': 'Low',
|
|
24
|
+
'mupen64plus-virefresh': 'Auto',
|
|
25
|
+
// Disable frame duping to ensure we always get frames
|
|
26
|
+
'mupen64plus-FrameDuping': 'False',
|
|
27
|
+
// Controller pak (memory pak enables save support)
|
|
28
|
+
'mupen64plus-pak1': 'memory',
|
|
29
|
+
'mupen64plus-pak2': 'none',
|
|
30
|
+
'mupen64plus-pak3': 'none',
|
|
31
|
+
'mupen64plus-pak4': 'none',
|
|
32
|
+
// Analog stick settings (deadzone 0 for full sensitivity)
|
|
33
|
+
'mupen64plus-astick-deadzone': '0',
|
|
34
|
+
'mupen64plus-astick-sensitivity': '100',
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Options Configuration
|
|
3
|
+
*
|
|
4
|
+
* Handles loading and saving libretro core options in RetroArch-compatible format.
|
|
5
|
+
* Options are stored in INI format with keys matching RetroArch conventions.
|
|
6
|
+
*
|
|
7
|
+
* Example format:
|
|
8
|
+
* mupen64plus-rdp-plugin = "angrylion"
|
|
9
|
+
* mupen64plus-rsp-plugin = "parallel"
|
|
10
|
+
* genesis_plus_gx-region_detect = "auto"
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
14
|
+
import { ensureDirectory } from '@/utils/ensureDirectory';
|
|
15
|
+
import { dirname, join } from "path";
|
|
16
|
+
import { pipe, filter, map, isNonNull, fromEntries } from "remeda";
|
|
17
|
+
import { getConfigDirectory } from "@/utils/paths";
|
|
18
|
+
import { parseIniLine, formatIniValue } from "@/utils/ini";
|
|
19
|
+
import { logger } from "@/utils/logger";
|
|
20
|
+
|
|
21
|
+
export * from './consts';
|
|
22
|
+
|
|
23
|
+
import { DEFAULT_CORE_OPTIONS } from './consts';
|
|
24
|
+
|
|
25
|
+
/** Normalize a core name to lowercase with underscores (e.g., "Mupen64Plus-Next" → "mupen64plus_next") */
|
|
26
|
+
const normalizeCoreName = (coreName: string): string =>
|
|
27
|
+
coreName.toLowerCase().replace(/[^a-z0-9]+/g, '_');
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Get the default path for the core options config file.
|
|
31
|
+
* Follows RetroArch convention: config_dir/retroarch-core-options.cfg
|
|
32
|
+
*/
|
|
33
|
+
export const getDefaultCoreOptionsPath = (): string =>
|
|
34
|
+
join(getConfigDirectory(), "retroarch-core-options.cfg");
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get the path for a core-specific options file.
|
|
38
|
+
* Format: config_dir/config/core_name/core_name.opt
|
|
39
|
+
*/
|
|
40
|
+
export const getCoreSpecificOptionsPath = (coreName: string): string =>
|
|
41
|
+
join(getConfigDirectory(), "config", coreName, `${coreName}.opt`);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get the path for a game-specific options file.
|
|
45
|
+
* Format: config_dir/config/core_name/game_name.opt
|
|
46
|
+
*/
|
|
47
|
+
export const getGameSpecificOptionsPath = (coreName: string, gameName: string): string =>
|
|
48
|
+
join(getConfigDirectory(), "config", coreName, `${gameName}.opt`);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Parse core options from a config file content.
|
|
52
|
+
* Only includes lines that look like core options (contain hyphens, typical of libretro options).
|
|
53
|
+
*
|
|
54
|
+
* @param content File content to parse
|
|
55
|
+
* @param corePrefix Optional prefix to filter options (e.g., "mupen64plus")
|
|
56
|
+
* @returns Record of option key-value pairs
|
|
57
|
+
*/
|
|
58
|
+
export const parseCoreOptionsContent = (
|
|
59
|
+
content: string,
|
|
60
|
+
corePrefix?: string
|
|
61
|
+
): Record<string, string> => {
|
|
62
|
+
const entries = pipe(
|
|
63
|
+
content.split('\n'),
|
|
64
|
+
map(parseIniLine),
|
|
65
|
+
filter(isNonNull),
|
|
66
|
+
// Core options typically have hyphens in their keys (e.g., "core-name-option")
|
|
67
|
+
filter(({ key }) => key.includes('-') || key.includes('_')),
|
|
68
|
+
// Optionally filter by core prefix
|
|
69
|
+
filter(({ key }) => !corePrefix || key.startsWith(corePrefix)),
|
|
70
|
+
map(({ key, value }) => [key, value] as const),
|
|
71
|
+
);
|
|
72
|
+
return fromEntries(entries) as Record<string, string>;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Load core options from a config file.
|
|
77
|
+
*
|
|
78
|
+
* @param filePath Path to the config file
|
|
79
|
+
* @param corePrefix Optional prefix to filter options
|
|
80
|
+
* @returns Record of option key-value pairs, or empty object if file doesn't exist
|
|
81
|
+
*/
|
|
82
|
+
export const loadCoreOptionsFile = (
|
|
83
|
+
filePath: string,
|
|
84
|
+
corePrefix?: string
|
|
85
|
+
): Record<string, string> => {
|
|
86
|
+
if (!existsSync(filePath)) {
|
|
87
|
+
return {};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const content = readFileSync(filePath, "utf-8");
|
|
92
|
+
const options = parseCoreOptionsContent(content, corePrefix);
|
|
93
|
+
|
|
94
|
+
if (Object.keys(options).length > 0) {
|
|
95
|
+
logger.debug(`Loaded ${Object.keys(options).length} core options from ${filePath}`, 'CoreOptions');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return options;
|
|
99
|
+
} catch (err) {
|
|
100
|
+
logger.warn(`Failed to load core options from ${filePath}: ${err}`, 'CoreOptions');
|
|
101
|
+
return {};
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Load core options with RetroArch-compatible precedence:
|
|
107
|
+
* 1. Game-specific options (highest priority)
|
|
108
|
+
* 2. Core-specific options
|
|
109
|
+
* 3. Global core options (lowest priority)
|
|
110
|
+
*
|
|
111
|
+
* @param coreName The core name (e.g., "mupen64plus_next")
|
|
112
|
+
* @param gameName Optional game name for game-specific options
|
|
113
|
+
* @returns Merged options with higher precedence overriding lower
|
|
114
|
+
*/
|
|
115
|
+
export const loadCoreOptions = (
|
|
116
|
+
coreName: string,
|
|
117
|
+
gameName?: string
|
|
118
|
+
): Record<string, string> => {
|
|
119
|
+
// Normalize core name (convert spaces/special chars to underscores)
|
|
120
|
+
const normalizedCoreName = normalizeCoreName(coreName);
|
|
121
|
+
|
|
122
|
+
// Extract core prefix from the core name (e.g., "mupen64plus" from "mupen64plus_next")
|
|
123
|
+
// This is used to filter options that belong to this core
|
|
124
|
+
const corePrefix = normalizedCoreName.split('_')[0];
|
|
125
|
+
|
|
126
|
+
// Load options in order of precedence (lowest to highest)
|
|
127
|
+
const globalOptions = loadCoreOptionsFile(getDefaultCoreOptionsPath(), corePrefix);
|
|
128
|
+
const coreOptions = loadCoreOptionsFile(getCoreSpecificOptionsPath(normalizedCoreName));
|
|
129
|
+
|
|
130
|
+
let gameOptions: Record<string, string> = {};
|
|
131
|
+
if (gameName) {
|
|
132
|
+
const normalizedGameName = gameName.replace(/\.[^.]+$/, ''); // Remove extension
|
|
133
|
+
gameOptions = loadCoreOptionsFile(getGameSpecificOptionsPath(normalizedCoreName, normalizedGameName));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Merge with higher precedence overriding lower
|
|
137
|
+
return {
|
|
138
|
+
...globalOptions,
|
|
139
|
+
...coreOptions,
|
|
140
|
+
...gameOptions,
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Save core options to the global core options file.
|
|
146
|
+
*
|
|
147
|
+
* @param options Options to save
|
|
148
|
+
* @param append If true, merge with existing options; if false, replace entirely
|
|
149
|
+
*/
|
|
150
|
+
export const saveCoreOptions = (
|
|
151
|
+
options: Record<string, string>,
|
|
152
|
+
append = true
|
|
153
|
+
): void => {
|
|
154
|
+
const filePath = getDefaultCoreOptionsPath();
|
|
155
|
+
const dir = dirname(filePath);
|
|
156
|
+
|
|
157
|
+
ensureDirectory(dir);
|
|
158
|
+
|
|
159
|
+
// Load existing options if appending
|
|
160
|
+
let mergedOptions = options;
|
|
161
|
+
if (append && existsSync(filePath)) {
|
|
162
|
+
const existing = loadCoreOptionsFile(filePath);
|
|
163
|
+
mergedOptions = { ...existing, ...options };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Format as INI content
|
|
167
|
+
const lines = Object.entries(mergedOptions)
|
|
168
|
+
.sort(([a], [b]) => a.localeCompare(b)) // Sort by key for consistency
|
|
169
|
+
.map(([key, value]) => `${key} = ${formatIniValue(value)}`);
|
|
170
|
+
|
|
171
|
+
const content = `# emoemu Core Options\n# RetroArch-compatible format\n\n${lines.join('\n')}\n`;
|
|
172
|
+
|
|
173
|
+
writeFileSync(filePath, content, "utf-8");
|
|
174
|
+
logger.info(`Saved ${Object.keys(mergedOptions).length} core options to ${filePath}`, 'CoreOptions');
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Save core-specific options to the core's config directory.
|
|
179
|
+
*
|
|
180
|
+
* @param coreName The core name
|
|
181
|
+
* @param options Options to save
|
|
182
|
+
*/
|
|
183
|
+
export const saveCoreSpecificOptions = (
|
|
184
|
+
coreName: string,
|
|
185
|
+
options: Record<string, string>
|
|
186
|
+
): void => {
|
|
187
|
+
const normalizedCoreName = normalizeCoreName(coreName);
|
|
188
|
+
const filePath = getCoreSpecificOptionsPath(normalizedCoreName);
|
|
189
|
+
const dir = dirname(filePath);
|
|
190
|
+
|
|
191
|
+
ensureDirectory(dir);
|
|
192
|
+
|
|
193
|
+
// Format as INI content
|
|
194
|
+
const lines = Object.entries(options)
|
|
195
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
196
|
+
.map(([key, value]) => `${key} = ${formatIniValue(value)}`);
|
|
197
|
+
|
|
198
|
+
const content = `# Core options for ${coreName}\n\n${lines.join('\n')}\n`;
|
|
199
|
+
|
|
200
|
+
writeFileSync(filePath, content, "utf-8");
|
|
201
|
+
logger.info(`Saved core-specific options to ${filePath}`, 'CoreOptions');
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get default core options for a given core name.
|
|
207
|
+
* Returns options needed for emoemu compatibility (e.g., software rendering).
|
|
208
|
+
*
|
|
209
|
+
* @param coreName The core's library name (e.g., "Mupen64Plus-Next")
|
|
210
|
+
* @returns Default options for the core, or undefined if none needed
|
|
211
|
+
*/
|
|
212
|
+
export const getDefaultCoreOptions = (coreName: string): Record<string, string> | undefined => {
|
|
213
|
+
const lowerName = coreName.toLowerCase();
|
|
214
|
+
|
|
215
|
+
for (const [pattern, options] of Object.entries(DEFAULT_CORE_OPTIONS)) {
|
|
216
|
+
if (lowerName.includes(pattern.toLowerCase())) {
|
|
217
|
+
return { ...options };
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return undefined;
|
|
222
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Environment Handler Constants
|
|
3
|
+
// =============================================================================
|
|
4
|
+
|
|
5
|
+
/** Maximum number of input players supported */
|
|
6
|
+
export const MAX_INPUT_USERS = 2;
|
|
7
|
+
|
|
8
|
+
/** Core options API version supported */
|
|
9
|
+
export const CORE_OPTIONS_VERSION = 2;
|
|
10
|
+
|
|
11
|
+
/** Message interface version supported */
|
|
12
|
+
export const MESSAGE_INTERFACE_VERSION = 1;
|
|
13
|
+
|
|
14
|
+
/** Audio enable bit (bit 1) */
|
|
15
|
+
export const AUDIO_ENABLE_BIT = 0b10;
|
|
16
|
+
|
|
17
|
+
/** Video enable bit (bit 0) */
|
|
18
|
+
export const VIDEO_ENABLE_BIT = 0b01;
|
|
19
|
+
|
|
20
|
+
// =============================================================================
|
|
21
|
+
// Memory Map Constants (64-bit system layout)
|
|
22
|
+
// =============================================================================
|
|
23
|
+
|
|
24
|
+
/** Size of retro_memory_map struct header (pointer + uint32) on 64-bit */
|
|
25
|
+
export const MEMORY_MAP_HEADER_SIZE = 16;
|
|
26
|
+
|
|
27
|
+
/** Byte offset of num_descriptors in retro_memory_map struct */
|
|
28
|
+
export const MEMORY_MAP_NUM_DESC_OFFSET = 8;
|
|
29
|
+
|
|
30
|
+
/** Size of retro_memory_descriptor struct on 64-bit system */
|
|
31
|
+
export const MEMORY_DESCRIPTOR_SIZE = 64;
|
|
32
|
+
|
|
33
|
+
/** Byte offset of 'len' field (size_t) in memory descriptor struct */
|
|
34
|
+
export const MEMORY_DESC_LEN_OFFSET = 48;
|
|
35
|
+
|
|
36
|
+
/** High word offset for 64-bit len field */
|
|
37
|
+
export const MEMORY_DESC_LEN_HIGH_OFFSET = 52;
|
|
38
|
+
|
|
39
|
+
/** Byte offset of 'ptr' field (void*) in memory descriptor struct */
|
|
40
|
+
export const MEMORY_DESC_PTR_OFFSET = 8;
|
|
41
|
+
|
|
42
|
+
/** Multiplier for combining high 32-bit word in 64-bit value */
|
|
43
|
+
export const UINT32_MULTIPLIER = 0x100000000;
|
|
44
|
+
|
|
45
|
+
/** Maximum descriptors to scan when looking for SRAM */
|
|
46
|
+
export const MAX_DESCRIPTORS_TO_SCAN = 10;
|
|
47
|
+
|
|
48
|
+
/** Pointer size on 64-bit system */
|
|
49
|
+
export const POINTER_SIZE_64BIT = 8;
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// Message Struct Constants (64-bit system layout)
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
/** Size of retro_message struct on 64-bit system (pointer + uint32 + padding) */
|
|
56
|
+
export const MESSAGE_STRUCT_SIZE = 16;
|
|
57
|
+
|
|
58
|
+
/** Byte offset of 'frames' field in retro_message struct */
|
|
59
|
+
export const MESSAGE_FRAMES_OFFSET = 8;
|
|
60
|
+
|
|
61
|
+
/** Size of retro_message_ext struct on 64-bit system */
|
|
62
|
+
export const MESSAGE_EXT_STRUCT_SIZE = 32;
|
|
63
|
+
|
|
64
|
+
/** Byte offset of 'duration' field in retro_message_ext struct */
|
|
65
|
+
export const MESSAGE_EXT_DURATION_OFFSET = 8;
|
|
66
|
+
|
|
67
|
+
/** Byte offset of 'priority' field in retro_message_ext struct */
|
|
68
|
+
export const MESSAGE_EXT_PRIORITY_OFFSET = 12;
|
|
69
|
+
|
|
70
|
+
/** Byte offset of 'level' field in retro_message_ext struct */
|
|
71
|
+
export const MESSAGE_EXT_LEVEL_OFFSET = 16;
|
|
72
|
+
|
|
73
|
+
/** Byte offset of 'target' field in retro_message_ext struct */
|
|
74
|
+
export const MESSAGE_EXT_TARGET_OFFSET = 20;
|
|
75
|
+
|
|
76
|
+
/** Byte offset of 'type' field in retro_message_ext struct */
|
|
77
|
+
export const MESSAGE_EXT_TYPE_OFFSET = 24;
|
|
78
|
+
|
|
79
|
+
/** Byte offset of 'progress' field in retro_message_ext struct */
|
|
80
|
+
export const MESSAGE_EXT_PROGRESS_OFFSET = 28;
|
|
81
|
+
|
|
82
|
+
// =============================================================================
|
|
83
|
+
// Memory Descriptor Flag Bit Positions
|
|
84
|
+
// =============================================================================
|
|
85
|
+
|
|
86
|
+
/** Bit position for RETRO_MEMDESC_SAVE_RAM flag */
|
|
87
|
+
export const MEMDESC_SAVE_RAM_BIT = 3;
|
|
88
|
+
|
|
89
|
+
/** Bit position for RETRO_MEMDESC_VIDEO_RAM flag */
|
|
90
|
+
export const MEMDESC_VIDEO_RAM_BIT = 4;
|
|
91
|
+
|
|
92
|
+
/** Bit position for alignment hints in memory descriptor flags */
|
|
93
|
+
export const MEMDESC_ALIGN_BIT = 16;
|
|
94
|
+
|
|
95
|
+
/** Bit position for minimum size hints in memory descriptor flags */
|
|
96
|
+
export const MEMDESC_MINSIZE_BIT = 24;
|
|
97
|
+
|
|
98
|
+
/** Alignment/size multiplier for 8-byte alignment */
|
|
99
|
+
export const MEMDESC_SIZE_8 = 3;
|
|
100
|
+
|
|
101
|
+
// =============================================================================
|
|
102
|
+
// Struct Size Constants
|
|
103
|
+
// =============================================================================
|
|
104
|
+
|
|
105
|
+
/** Size of a uint32 in bytes */
|
|
106
|
+
export const UINT32_SIZE = 4;
|
|
107
|
+
|
|
108
|
+
/** Number of uint32 fields before aspect_ratio in retro_game_geometry struct */
|
|
109
|
+
export const GEOMETRY_UINT_COUNT = 4;
|
|
110
|
+
|
|
111
|
+
/** Struct padding for alignment in retro_controller_info (4 bytes) */
|
|
112
|
+
export const STRUCT_PADDING_4 = 4;
|
|
113
|
+
|
|
114
|
+
/** Maximum controller types to read per port (safety limit) */
|
|
115
|
+
export const MAX_CONTROLLER_TYPES = 20;
|
|
116
|
+
|
|
117
|
+
/** Decimal places for aspect ratio formatting */
|
|
118
|
+
export const ASPECT_RATIO_DECIMALS = 3;
|