fix: switched to node-7z
fix: switched to bun spawn but with windowsVerbatimArguments feat: Added ppsspp integration feat: Added focusing controls for windows feat: Added shortcut to kill emulators
This commit is contained in:
parent
a7eb655a48
commit
90d6711935
31 changed files with 1382 additions and 88 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -27,3 +27,5 @@ downloads
|
||||||
.flatpak-builder
|
.flatpak-builder
|
||||||
gameflow-deck.code-workspace
|
gameflow-deck.code-workspace
|
||||||
.env.local
|
.env.local
|
||||||
|
src/tests/mock-roms/db.sqlite
|
||||||
|
src/tests/mock-config
|
||||||
33
bun.lock
33
bun.lock
|
|
@ -5,19 +5,22 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "electrobun-hello-world",
|
"name": "electrobun-hello-world",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"7zip-min": "^3.0.1",
|
"7zip-bin": "^5.2.0",
|
||||||
"@auth/core": "^0.34.3",
|
"@auth/core": "^0.34.3",
|
||||||
"@elysiajs/cors": "^1.4.1",
|
"@elysiajs/cors": "^1.4.1",
|
||||||
"@elysiajs/eden": "^1.4.6",
|
"@elysiajs/eden": "^1.4.6",
|
||||||
"@jimp/wasm-webp": "^1.6.0",
|
"@jimp/wasm-webp": "^1.6.0",
|
||||||
|
"@kmamal/sdl": "^0.11.13",
|
||||||
"cheerio": "^1.2.0",
|
"cheerio": "^1.2.0",
|
||||||
"conf": "^15.0.2",
|
"conf": "^15.0.2",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"elysia": "^1.4.22",
|
"elysia": "^1.4.22",
|
||||||
"fs-extra": "^11.3.3",
|
"fs-extra": "^11.3.3",
|
||||||
"get-folder-size": "^5.0.0",
|
"get-folder-size": "^5.0.0",
|
||||||
|
"ini": "^6.0.0",
|
||||||
"jimp": "^1.6.0",
|
"jimp": "^1.6.0",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "^4.2.0",
|
||||||
|
"node-7z": "^3.0.0",
|
||||||
"node-disk-info": "^1.3.0",
|
"node-disk-info": "^1.3.0",
|
||||||
"node-downloader-helper": "^2.1.10",
|
"node-downloader-helper": "^2.1.10",
|
||||||
"node-stream-zip": "^1.15.0",
|
"node-stream-zip": "^1.15.0",
|
||||||
|
|
@ -49,7 +52,9 @@
|
||||||
"@tanstack/zod-adapter": "^1.162.4",
|
"@tanstack/zod-adapter": "^1.162.4",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"@types/fs-extra": "^11.0.4",
|
"@types/fs-extra": "^11.0.4",
|
||||||
|
"@types/ini": "^4.1.1",
|
||||||
"@types/mustache": "^4.2.6",
|
"@types/mustache": "^4.2.6",
|
||||||
|
"@types/node-7z": "^2.1.11",
|
||||||
"@types/react": "^19.2.9",
|
"@types/react": "^19.2.9",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@types/unzip-stream": "^0.3.4",
|
"@types/unzip-stream": "^0.3.4",
|
||||||
|
|
@ -86,9 +91,7 @@
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"packages": {
|
"packages": {
|
||||||
"7zip-bin": ["7zip-bin@5.1.1", "", {}, "sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ=="],
|
"7zip-bin": ["7zip-bin@5.2.0", "", {}, "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A=="],
|
||||||
|
|
||||||
"7zip-min": ["7zip-min@3.0.1", "", { "dependencies": { "7zip-bin": "5.1.1" } }, "sha512-WB4VCA/KSKzxhj+BAp8fI3ZYMMAftclkXlUTckuiDacsqyubQxxG3lGcpBcgzWWuJqnfQncEq1xrJpPLSxqsxw=="],
|
|
||||||
|
|
||||||
"@ap0nia/eden": ["@ap0nia/eden@1.0.0-next.22", "", { "peerDependencies": { "elysia": "^1.3.1" } }, "sha512-9iH09koK29Yuem80fz8nCt9iHVcJqxUo2QHAr4psI02PhvL70n6aWVo/hlHyYXwOSsSgRQlLl1vPmiulFOUFoA=="],
|
"@ap0nia/eden": ["@ap0nia/eden@1.0.0-next.22", "", { "peerDependencies": { "elysia": "^1.3.1" } }, "sha512-9iH09koK29Yuem80fz8nCt9iHVcJqxUo2QHAr4psI02PhvL70n6aWVo/hlHyYXwOSsSgRQlLl1vPmiulFOUFoA=="],
|
||||||
|
|
||||||
|
|
@ -324,6 +327,8 @@
|
||||||
|
|
||||||
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
"@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="],
|
||||||
|
|
||||||
|
"@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="],
|
||||||
|
|
||||||
"@jimp/core": ["@jimp/core@1.6.0", "", { "dependencies": { "@jimp/file-ops": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "await-to-js": "^3.0.0", "exif-parser": "^0.1.12", "file-type": "^16.0.0", "mime": "3" } }, "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w=="],
|
"@jimp/core": ["@jimp/core@1.6.0", "", { "dependencies": { "@jimp/file-ops": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "await-to-js": "^3.0.0", "exif-parser": "^0.1.12", "file-type": "^16.0.0", "mime": "3" } }, "sha512-EQQlKU3s9QfdJqiSrZWNTxBs3rKXgO2W+GxNXDtwchF3a4IqxDheFX1ti+Env9hdJXDiYLp2jTRjlxhPthsk8w=="],
|
||||||
|
|
||||||
"@jimp/diff": ["@jimp/diff@1.6.0", "", { "dependencies": { "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "pixelmatch": "^5.3.0" } }, "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw=="],
|
"@jimp/diff": ["@jimp/diff@1.6.0", "", { "dependencies": { "@jimp/plugin-resize": "1.6.0", "@jimp/types": "1.6.0", "@jimp/utils": "1.6.0", "pixelmatch": "^5.3.0" } }, "sha512-+yUAQ5gvRC5D1WHYxjBHZI7JBRusGGSLf8AmPRPCenTzh4PA+wZ1xv2+cYqQwTfQHU5tXYOhA0xDytfHUf1Zyw=="],
|
||||||
|
|
@ -398,6 +403,8 @@
|
||||||
|
|
||||||
"@jsquash/webp": ["@jsquash/webp@1.5.0", "", { "dependencies": { "wasm-feature-detect": "^1.2.11" } }, "sha512-KggLoj2MnRSfIqTeKe1EmbljTX2vuV7mh79k89PCL1pyqiDULcPM1L47twxXt0hkb68F70bXiL31MxsuoZtKFw=="],
|
"@jsquash/webp": ["@jsquash/webp@1.5.0", "", { "dependencies": { "wasm-feature-detect": "^1.2.11" } }, "sha512-KggLoj2MnRSfIqTeKe1EmbljTX2vuV7mh79k89PCL1pyqiDULcPM1L47twxXt0hkb68F70bXiL31MxsuoZtKFw=="],
|
||||||
|
|
||||||
|
"@kmamal/sdl": ["@kmamal/sdl@0.11.13", "", { "dependencies": { "tar": "^7.4.3" } }, "sha512-9WmxYNtCggi7Ovq1cU7m/s5WXD/+eKQxDMnL3bU8B5vr5GlaLg4xLykDCpcbWKkJJ2i6llTrdL7LiqikwDFz4w=="],
|
||||||
|
|
||||||
"@node-minify/clean-css": ["@node-minify/clean-css@9.0.1", "", { "dependencies": { "@node-minify/utils": "9.0.1", "clean-css": "5.3.3" } }, "sha512-GHTMmjGloRvNzqdG7foI0iZeS2QmuYCQvdASJP9sCKjkpH45bygODpXPYKnlzUEpQgYvPK9Q3GxqYnVY9SdoqA=="],
|
"@node-minify/clean-css": ["@node-minify/clean-css@9.0.1", "", { "dependencies": { "@node-minify/utils": "9.0.1", "clean-css": "5.3.3" } }, "sha512-GHTMmjGloRvNzqdG7foI0iZeS2QmuYCQvdASJP9sCKjkpH45bygODpXPYKnlzUEpQgYvPK9Q3GxqYnVY9SdoqA=="],
|
||||||
|
|
||||||
"@node-minify/core": ["@node-minify/core@9.0.2", "", { "dependencies": { "@node-minify/utils": "9.0.1", "glob": "10.3.3", "mkdirp": "3.0.1" } }, "sha512-FNhv29Wom6wKrrFKaeAfmZqz7TX5A1E6P+bpd0VIc+DYWMLUIhAViS8riaZg3A1oD0s06s+5BG2Fg7RqMKiKHw=="],
|
"@node-minify/core": ["@node-minify/core@9.0.2", "", { "dependencies": { "@node-minify/utils": "9.0.1", "glob": "10.3.3", "mkdirp": "3.0.1" } }, "sha512-FNhv29Wom6wKrrFKaeAfmZqz7TX5A1E6P+bpd0VIc+DYWMLUIhAViS8riaZg3A1oD0s06s+5BG2Fg7RqMKiKHw=="],
|
||||||
|
|
@ -600,6 +607,8 @@
|
||||||
|
|
||||||
"@types/fs-extra": ["@types/fs-extra@11.0.4", "", { "dependencies": { "@types/jsonfile": "*", "@types/node": "*" } }, "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ=="],
|
"@types/fs-extra": ["@types/fs-extra@11.0.4", "", { "dependencies": { "@types/jsonfile": "*", "@types/node": "*" } }, "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ=="],
|
||||||
|
|
||||||
|
"@types/ini": ["@types/ini@4.1.1", "", {}, "sha512-MIyNUZipBTbyUNnhvuXJTY7B6qNI78meck9Jbv3wk0OgNwRyOOVEKDutAkOs1snB/tx0FafyR6/SN4Ps0hZPeg=="],
|
||||||
|
|
||||||
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
|
||||||
|
|
||||||
"@types/jsonfile": ["@types/jsonfile@6.1.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ=="],
|
"@types/jsonfile": ["@types/jsonfile@6.1.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ=="],
|
||||||
|
|
@ -610,6 +619,8 @@
|
||||||
|
|
||||||
"@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
|
"@types/node": ["@types/node@24.1.0", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w=="],
|
||||||
|
|
||||||
|
"@types/node-7z": ["@types/node-7z@2.1.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-7gwLx44tqZqjyrvjkX41CWW4h7+aXrazFg/JR6N5g+R5BW1eqsNuw8SNLWrh7KcnfKhAYFiWyNb10ti5v5eCmQ=="],
|
||||||
|
|
||||||
"@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="],
|
"@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.2.9", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA=="],
|
"@types/react": ["@types/react@19.2.9", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA=="],
|
||||||
|
|
@ -728,6 +739,8 @@
|
||||||
|
|
||||||
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
"chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
||||||
|
|
||||||
|
"chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="],
|
||||||
|
|
||||||
"citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
|
"citty": ["citty@0.1.6", "", { "dependencies": { "consola": "^3.2.3" } }, "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ=="],
|
||||||
|
|
||||||
"classnames": ["classnames@2.5.1", "", {}, "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="],
|
"classnames": ["classnames@2.5.1", "", {}, "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="],
|
||||||
|
|
@ -1050,7 +1063,7 @@
|
||||||
|
|
||||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||||
|
|
||||||
"ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
|
"ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="],
|
||||||
|
|
||||||
"is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
|
"is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
|
||||||
|
|
||||||
|
|
@ -1206,6 +1219,8 @@
|
||||||
|
|
||||||
"minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
|
"minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
|
||||||
|
|
||||||
|
"minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="],
|
||||||
|
|
||||||
"mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
"mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="],
|
||||||
|
|
||||||
"modify-values": ["modify-values@1.0.1", "", {}, "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw=="],
|
"modify-values": ["modify-values@1.0.1", "", {}, "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw=="],
|
||||||
|
|
@ -1544,6 +1559,8 @@
|
||||||
|
|
||||||
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
||||||
|
|
||||||
|
"tar": ["tar@7.5.13", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng=="],
|
||||||
|
|
||||||
"terser": ["terser@5.46.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg=="],
|
"terser": ["terser@5.46.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg=="],
|
||||||
|
|
||||||
"text-extensions": ["text-extensions@1.9.0", "", {}, "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ=="],
|
"text-extensions": ["text-extensions@1.9.0", "", {}, "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ=="],
|
||||||
|
|
@ -1674,7 +1691,7 @@
|
||||||
|
|
||||||
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
"y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="],
|
||||||
|
|
||||||
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
"yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="],
|
||||||
|
|
||||||
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
"yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="],
|
||||||
|
|
||||||
|
|
@ -1788,6 +1805,8 @@
|
||||||
|
|
||||||
"git-semver-tags/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
"git-semver-tags/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
|
||||||
|
|
||||||
|
"gitconfiglocal/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
|
||||||
|
|
||||||
"glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
"glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
|
||||||
|
|
||||||
"handlebars/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
"handlebars/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||||
|
|
@ -1802,6 +1821,8 @@
|
||||||
|
|
||||||
"load-json-file/pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="],
|
"load-json-file/pify": ["pify@3.0.0", "", {}, "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg=="],
|
||||||
|
|
||||||
|
"lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||||
|
|
||||||
"meow/read-pkg-up": ["read-pkg-up@7.0.1", "", { "dependencies": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", "type-fest": "^0.8.1" } }, "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg=="],
|
"meow/read-pkg-up": ["read-pkg-up@7.0.1", "", { "dependencies": { "find-up": "^4.1.0", "read-pkg": "^5.2.0", "type-fest": "^0.8.1" } }, "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg=="],
|
||||||
|
|
||||||
"meow/type-fest": ["type-fest@0.18.1", "", {}, "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="],
|
"meow/type-fest": ["type-fest@0.18.1", "", {}, "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw=="],
|
||||||
|
|
|
||||||
11
package.json
11
package.json
|
|
@ -13,8 +13,10 @@
|
||||||
"packageManager": "bun@1.3.9",
|
"packageManager": "bun@1.3.9",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": " NODE_ENV=development bun run build:vite && conc 'bun run ./scripts/dev.ts'",
|
"dev": "NODE_ENV=development bun run build:vite && conc 'bun run ./scripts/dev.ts'",
|
||||||
"dev:hmr": "PUBLIC_ACCESS=true conc -k 'bun run hmr' 'bun run ./scripts/dev.ts'",
|
"dev:hmr": "PUBLIC_ACCESS=true conc -k 'bun run hmr' 'bun run ./scripts/dev.ts'",
|
||||||
|
"dev:bun:hmr": "PUBLIC_ACCESS=true NODE_ENV=development conc 'bun run hmr' 'bun run --watch ./src/bun/index.ts",
|
||||||
|
"dev:bun": "NODE_ENV=development bun run build:vite && conc 'bun run ./src/bun/index.ts",
|
||||||
"build:vite": "bun run --bun vite build",
|
"build:vite": "bun run --bun vite build",
|
||||||
"build:prod:vite": "NODE_ENV=production bun run build:vite",
|
"build:prod:vite": "NODE_ENV=production bun run build:vite",
|
||||||
"build:dev:vite": "NODE_ENV=development bun run build:vite",
|
"build:dev:vite": "NODE_ENV=development bun run build:vite",
|
||||||
|
|
@ -40,19 +42,22 @@
|
||||||
"package:Windows": "bun run build:prod"
|
"package:Windows": "bun run build:prod"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"7zip-min": "^3.0.1",
|
"7zip-bin": "^5.2.0",
|
||||||
"@auth/core": "^0.34.3",
|
"@auth/core": "^0.34.3",
|
||||||
"@elysiajs/cors": "^1.4.1",
|
"@elysiajs/cors": "^1.4.1",
|
||||||
"@elysiajs/eden": "^1.4.6",
|
"@elysiajs/eden": "^1.4.6",
|
||||||
"@jimp/wasm-webp": "^1.6.0",
|
"@jimp/wasm-webp": "^1.6.0",
|
||||||
|
"@kmamal/sdl": "^0.11.13",
|
||||||
"cheerio": "^1.2.0",
|
"cheerio": "^1.2.0",
|
||||||
"conf": "^15.0.2",
|
"conf": "^15.0.2",
|
||||||
"drizzle-orm": "^0.45.1",
|
"drizzle-orm": "^0.45.1",
|
||||||
"elysia": "^1.4.22",
|
"elysia": "^1.4.22",
|
||||||
"fs-extra": "^11.3.3",
|
"fs-extra": "^11.3.3",
|
||||||
"get-folder-size": "^5.0.0",
|
"get-folder-size": "^5.0.0",
|
||||||
|
"ini": "^6.0.0",
|
||||||
"jimp": "^1.6.0",
|
"jimp": "^1.6.0",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "^4.2.0",
|
||||||
|
"node-7z": "^3.0.0",
|
||||||
"node-disk-info": "^1.3.0",
|
"node-disk-info": "^1.3.0",
|
||||||
"node-downloader-helper": "^2.1.10",
|
"node-downloader-helper": "^2.1.10",
|
||||||
"node-stream-zip": "^1.15.0",
|
"node-stream-zip": "^1.15.0",
|
||||||
|
|
@ -84,7 +89,9 @@
|
||||||
"@tanstack/zod-adapter": "^1.162.4",
|
"@tanstack/zod-adapter": "^1.162.4",
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"@types/fs-extra": "^11.0.4",
|
"@types/fs-extra": "^11.0.4",
|
||||||
|
"@types/ini": "^4.1.1",
|
||||||
"@types/mustache": "^4.2.6",
|
"@types/mustache": "^4.2.6",
|
||||||
|
"@types/node-7z": "^2.1.11",
|
||||||
"@types/react": "^19.2.9",
|
"@types/react": "^19.2.9",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@types/unzip-stream": "^0.3.4",
|
"@types/unzip-stream": "^0.3.4",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ import EventEmitter from "events";
|
||||||
import browser from '../src/bun/browser';
|
import browser from '../src/bun/browser';
|
||||||
import { tmpdir } from "os";
|
import { tmpdir } from "os";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
import { createInterface } from "readline";
|
||||||
|
import { Readable } from "stream";
|
||||||
const events = new EventEmitter();
|
const events = new EventEmitter();
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
|
|
||||||
|
|
@ -12,23 +14,16 @@ let retries = 0;
|
||||||
|
|
||||||
function spawnServer ()
|
function spawnServer ()
|
||||||
{
|
{
|
||||||
return Bun.spawn(["bun", '--watch', '--install=fallback', "run", "--inspect=127.0.0.1:9228/fixed-session", "./src/bun/index.ts"], {
|
const s = Bun.spawn(["bun", '--watch', '--install=fallback', "run", "--inspect=127.0.0.1:9228/fixed-session", "./src/bun/index.ts"], {
|
||||||
env: {
|
env: {
|
||||||
...process.env,
|
...process.env,
|
||||||
HEADLESS: "true",
|
HEADLESS: "true",
|
||||||
},
|
},
|
||||||
stdout: "inherit",
|
stdout: "pipe",
|
||||||
stderr: "inherit",
|
stderr: "inherit",
|
||||||
stdin: "pipe",
|
stdin: "pipe",
|
||||||
signal: abortController.signal,
|
signal: abortController.signal,
|
||||||
killSignal: 'SIGUSR1',
|
killSignal: 'SIGUSR1',
|
||||||
ipc (message, subprocess, handle)
|
|
||||||
{
|
|
||||||
if (message.type === 'exitapp')
|
|
||||||
{
|
|
||||||
events.emit('exitapp');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onExit (subprocess, exitCode, signalCode)
|
onExit (subprocess, exitCode, signalCode)
|
||||||
{
|
{
|
||||||
if (exitCode === 1 && retries <= 3)
|
if (exitCode === 1 && retries <= 3)
|
||||||
|
|
@ -42,6 +37,18 @@ function spawnServer ()
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const rl = createInterface({ input: Readable.fromWeb(s.stdout as any) });
|
||||||
|
rl.on('line', e =>
|
||||||
|
{
|
||||||
|
if (e === 'focus')
|
||||||
|
{
|
||||||
|
events.emit('focus');
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
function spawnBrowser ()
|
function spawnBrowser ()
|
||||||
|
|
|
||||||
|
|
@ -26,13 +26,30 @@ if (process.env.TARGET)
|
||||||
compileOption.target = process.env.TARGET as any;
|
compileOption.target = process.env.TARGET as any;
|
||||||
}
|
}
|
||||||
|
|
||||||
let webviewLib = "libwebview.dll";
|
|
||||||
if (process.platform === 'linux' && system.arch === 'x64')
|
let zip: string | undefined;
|
||||||
webviewLib = "libwebview-x64.so";
|
let zipNodePath: string | undefined;
|
||||||
if (process.platform === 'linux' && system.arch === 'arm64')
|
let webviewLib: string | undefined;
|
||||||
webviewLib = "libwebview-arm64.so";
|
switch (process.platform)
|
||||||
if (process.platform === 'darwin')
|
{
|
||||||
webviewLib = "libwebview-arm64.dylib";
|
case "win32":
|
||||||
|
zip = "7za.exe";
|
||||||
|
zipNodePath = "win";
|
||||||
|
webviewLib = `libwebview.dll`;
|
||||||
|
break;
|
||||||
|
case "linux":
|
||||||
|
zip = "7za";
|
||||||
|
zipNodePath = 'linux';
|
||||||
|
webviewLib = `libwebview-${system.arch}.so`;
|
||||||
|
break;
|
||||||
|
case "darwin":
|
||||||
|
zip = "7za";
|
||||||
|
zipNodePath = 'mac';
|
||||||
|
webviewLib = `libwebview-${system.arch}.dylib`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!webviewLib) throw new Error("Could not find webviewlib");
|
||||||
|
|
||||||
let webviewLibPath = '.';
|
let webviewLibPath = '.';
|
||||||
if (process.env.APPIMAGE === "true")
|
if (process.env.APPIMAGE === "true")
|
||||||
|
|
@ -47,6 +64,7 @@ await Bun.build({
|
||||||
define: {
|
define: {
|
||||||
"process.env.IS_BINARY": "true",
|
"process.env.IS_BINARY": "true",
|
||||||
"process.env.WEBVIEW_PATH": `${webviewLibPath}/${webviewLib}`,
|
"process.env.WEBVIEW_PATH": `${webviewLibPath}/${webviewLib}`,
|
||||||
|
"process.env.ZIP7_PATH": `"${zip}"`
|
||||||
},
|
},
|
||||||
minify: process.env.NODE_ENV !== 'development',
|
minify: process.env.NODE_ENV !== 'development',
|
||||||
sourcemap: process.env.NODE_ENV === 'development' ? 'inline' : "linked",
|
sourcemap: process.env.NODE_ENV === 'development' ? 'inline' : "linked",
|
||||||
|
|
@ -77,6 +95,8 @@ await Bun.build({
|
||||||
await fs.cp('./drizzle', `${buildSubDir}/drizzle`, { recursive: true });
|
await fs.cp('./drizzle', `${buildSubDir}/drizzle`, { recursive: true });
|
||||||
await fs.cp(`./vendors/es-de/emulators.${system.platform}.${system.arch}.sqlite`, `${buildSubDir}/vendors/es-de/emulators.${system.platform}.${system.arch}.sqlite`, { recursive: true });
|
await fs.cp(`./vendors/es-de/emulators.${system.platform}.${system.arch}.sqlite`, `${buildSubDir}/vendors/es-de/emulators.${system.platform}.${system.arch}.sqlite`, { recursive: true });
|
||||||
await fs.cp(path.join(`node_modules/webview-bun/build/`, webviewLib), path.join(buildSubDir, webviewLib));
|
await fs.cp(path.join(`node_modules/webview-bun/build/`, webviewLib), path.join(buildSubDir, webviewLib));
|
||||||
|
await fs.cp(`node_modules/@kmamal/sdl/dist`, buildSubDir, { recursive: true, errorOnExist: false });
|
||||||
|
await fs.cp(`node_modules/7zip-bin/${zipNodePath}/${process.arch}`, buildSubDir, { recursive: true, errorOnExist: false });
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}]
|
}]
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,12 @@ import UpdateStoreJob from "./jobs/update-store";
|
||||||
import { getStoreFolder } from "./store/services/gamesService";
|
import { getStoreFolder } from "./store/services/gamesService";
|
||||||
import { PluginManager } from "./plugins/plugin-manager";
|
import { PluginManager } from "./plugins/plugin-manager";
|
||||||
import registerPlugins from "./plugins/register-plugins";
|
import registerPlugins from "./plugins/register-plugins";
|
||||||
|
import controls from '../controls';
|
||||||
|
|
||||||
export const config = new Conf<SettingsType>({
|
export const config = new Conf<SettingsType>({
|
||||||
projectName: projectPackage.name,
|
projectName: projectPackage.name,
|
||||||
projectSuffix: 'bun',
|
projectSuffix: 'bun',
|
||||||
|
cwd: process.env.CONFIG_CWD,
|
||||||
schema: Object.fromEntries(Object.entries(SettingsSchema.shape).map(([key, schema]) => [key, schema.toJSONSchema() as any])) as any,
|
schema: Object.fromEntries(Object.entries(SettingsSchema.shape).map(([key, schema]) => [key, schema.toJSONSchema() as any])) as any,
|
||||||
defaults: SettingsSchema.parse({
|
defaults: SettingsSchema.parse({
|
||||||
downloadPath: path.join(os.homedir(), "gameflow"),
|
downloadPath: path.join(os.homedir(), "gameflow"),
|
||||||
|
|
@ -35,6 +37,7 @@ export const config = new Conf<SettingsType>({
|
||||||
export const customEmulators = new Conf<Record<string, string>>({
|
export const customEmulators = new Conf<Record<string, string>>({
|
||||||
projectName: projectPackage.name,
|
projectName: projectPackage.name,
|
||||||
projectSuffix: 'bun',
|
projectSuffix: 'bun',
|
||||||
|
cwd: process.env.CONFIG_CWD,
|
||||||
configName: 'custom-emulators',
|
configName: 'custom-emulators',
|
||||||
rootSchema: {
|
rootSchema: {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
@ -67,6 +70,7 @@ registerPlugins(plugins);
|
||||||
export const events = new EventEmitter<AppEventMap>();
|
export const events = new EventEmitter<AppEventMap>();
|
||||||
config.onDidChange('downloadPath', () => reloadDatabase());
|
config.onDidChange('downloadPath', () => reloadDatabase());
|
||||||
taskQueue.enqueue(UpdateStoreJob.id, new UpdateStoreJob());
|
taskQueue.enqueue(UpdateStoreJob.id, new UpdateStoreJob());
|
||||||
|
await controls();
|
||||||
|
|
||||||
export async function cleanup ()
|
export async function cleanup ()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -73,10 +73,6 @@ export async function getEmulatorsForSystem (systemSlug: string)
|
||||||
export async function getValidLaunchCommands (data: {
|
export async function getValidLaunchCommands (data: {
|
||||||
systemSlug: string;
|
systemSlug: string;
|
||||||
gamePath: string;
|
gamePath: string;
|
||||||
customEmulatorConfig: {
|
|
||||||
get: (id: string) => string | undefined,
|
|
||||||
has: (id: string) => boolean,
|
|
||||||
};
|
|
||||||
}): Promise<CommandEntry[]>
|
}): Promise<CommandEntry[]>
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { Glob } from "bun";
|
||||||
import { config } from "../app";
|
import { config } from "../app";
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { getOrCachedGithubRelease } from "../cache";
|
import { getOrCachedGithubRelease } from "../cache";
|
||||||
import _7z from '7zip-min';
|
import Seven from 'node-7z';
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import { Downloader } from "@/bun/utils/downloader";
|
import { Downloader } from "@/bun/utils/downloader";
|
||||||
import { move } from "fs-extra";
|
import { move } from "fs-extra";
|
||||||
|
|
@ -85,7 +85,13 @@ export class EmulatorDownloadJob implements IJob<z.infer<typeof EmulatorDownload
|
||||||
if (await downloader.start() && destinationPaths[0])
|
if (await downloader.start() && destinationPaths[0])
|
||||||
{
|
{
|
||||||
let destinationPath = destinationPaths[0];
|
let destinationPath = destinationPaths[0];
|
||||||
await _7z.unpack(destinationPath, emulatorsFolder);
|
await new Promise((resolve, reject) =>
|
||||||
|
{
|
||||||
|
const seven = Seven.extractFull(destinationPath, emulatorsFolder, { $bin: process.env.ZIP7_PATH, $progress: true });
|
||||||
|
seven.on('progress', p => context.setProgress(p.percent, "extract"));
|
||||||
|
seven.on('error', e => reject(e));
|
||||||
|
seven.on('end', () => resolve(true));
|
||||||
|
});
|
||||||
await fs.rm(destinationPath, { recursive: true });
|
await fs.rm(destinationPath, { recursive: true });
|
||||||
|
|
||||||
// check if 1 root folder we need to get rid of
|
// check if 1 root folder we need to get rid of
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { IJob, JobContext } from "../task-queue";
|
import { IJob, JobContext } from "../task-queue";
|
||||||
import { mkdir } from 'node:fs/promises';
|
|
||||||
import { and, eq, or } from 'drizzle-orm';
|
import { and, eq, or } from 'drizzle-orm';
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import * as schema from "@schema/app";
|
import * as schema from "@schema/app";
|
||||||
|
|
@ -11,11 +10,10 @@ import * as igdb from 'ts-igdb-client';
|
||||||
import secrets from "../secrets";
|
import secrets from "../secrets";
|
||||||
import { simulateProgress } from "@/bun/utils";
|
import { simulateProgress } from "@/bun/utils";
|
||||||
import { Downloader } from "@/bun/utils/downloader";
|
import { Downloader } from "@/bun/utils/downloader";
|
||||||
import _7z from '7zip-min';
|
import Seven from 'node-7z';
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import { checkFiles } from "../games/services/utils";
|
import { checkFiles } from "../games/services/utils";
|
||||||
import { ensureDir } from "fs-extra";
|
import { ensureDir } from "fs-extra";
|
||||||
import { getAuthToken } from "@/clients/romm/core/auth.gen";
|
|
||||||
|
|
||||||
interface JobConfig
|
interface JobConfig
|
||||||
{
|
{
|
||||||
|
|
@ -105,9 +103,19 @@ export class InstallJob implements IJob<never, InstallJobStates>
|
||||||
const downloadedFiles = await downloader.start();
|
const downloadedFiles = await downloader.start();
|
||||||
if (info.extract_path && downloadedFiles)
|
if (info.extract_path && downloadedFiles)
|
||||||
{
|
{
|
||||||
|
let progress = 0;
|
||||||
|
const progressDelta = 1 / downloadedFiles.length;
|
||||||
for (const path of downloadedFiles)
|
for (const path of downloadedFiles)
|
||||||
{
|
{
|
||||||
await _7z.unpack(path, info.extract_path);
|
const extractPath = info.extract_path;
|
||||||
|
await new Promise((resolve, reject) =>
|
||||||
|
{
|
||||||
|
const seven = Seven.extractFull(path, extractPath, { $bin: process.env.ZIP7_PATH, $progress: true });
|
||||||
|
seven.on('progress', p => cx.setProgress(progress + p.percent * progressDelta, "extract"));
|
||||||
|
seven.on('error', e => reject(e));
|
||||||
|
seven.on('end', () => resolve(true));
|
||||||
|
});
|
||||||
|
progress += progressDelta * 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ import { ActiveGameSchema, ActiveGameType } from "@/bun/types/typesc.schema";
|
||||||
import { db, events, plugins } from "../app";
|
import { db, events, plugins } from "../app";
|
||||||
import * as appSchema from "@schema/app";
|
import * as appSchema from "@schema/app";
|
||||||
import { eq, sql } from "drizzle-orm";
|
import { eq, sql } from "drizzle-orm";
|
||||||
import { spawn } from 'node:child_process';
|
import { ChildProcessWithoutNullStreams, spawn } from 'node:child_process';
|
||||||
|
import { killBrowser } from "@/bun/utils/browser-spawner";
|
||||||
|
|
||||||
export class LaunchGameJob implements IJob<z.infer<typeof LaunchGameJob.dataSchema>, "playing">
|
export class LaunchGameJob implements IJob<z.infer<typeof LaunchGameJob.dataSchema>, "playing">
|
||||||
{
|
{
|
||||||
|
|
@ -39,26 +40,42 @@ export class LaunchGameJob implements IJob<z.infer<typeof LaunchGameJob.dataSche
|
||||||
autoValidCommand: this.validCommand,
|
autoValidCommand: this.validCommand,
|
||||||
game: { source: this.gameSource, id: this.gameId }
|
game: { source: this.gameSource, id: this.gameId }
|
||||||
});
|
});
|
||||||
const command = commandArgs ? this.validCommand.metadata.emulatorBin ?? this.validCommand.command : this.validCommand.command;
|
|
||||||
|
|
||||||
await new Promise((resolve, reject) =>
|
await new Promise((resolve, reject) =>
|
||||||
{
|
{
|
||||||
const game = spawn(command, commandArgs, {
|
let game: Bun.Subprocess;
|
||||||
shell: true,
|
if (!commandArgs)
|
||||||
|
{
|
||||||
|
game = Bun.spawn(this.validCommand.command.split(' '), {
|
||||||
cwd: this.validCommand.startDir,
|
cwd: this.validCommand.startDir,
|
||||||
|
windowsVerbatimArguments: true,
|
||||||
signal: context.abortSignal
|
signal: context.abortSignal
|
||||||
});
|
});
|
||||||
|
|
||||||
game.stdout.on('data', data => console.log(data));
|
game.exited.then(resolve).catch(e =>
|
||||||
game.on('close', (code) =>
|
|
||||||
{
|
|
||||||
resolve(code);
|
|
||||||
});
|
|
||||||
game.on('error', e =>
|
|
||||||
{
|
{
|
||||||
console.error(e);
|
console.error(e);
|
||||||
reject(e);
|
reject(e);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
else if (this.validCommand.metadata.emulatorBin)
|
||||||
|
{
|
||||||
|
game = Bun.spawn([this.validCommand.metadata.emulatorBin, ...commandArgs], {
|
||||||
|
cwd: this.validCommand.startDir,
|
||||||
|
windowsVerbatimArguments: true,
|
||||||
|
signal: context.abortSignal
|
||||||
|
});
|
||||||
|
|
||||||
|
game.exited.then(resolve).catch(e =>
|
||||||
|
{
|
||||||
|
console.error(e);
|
||||||
|
reject(e);
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
reject(new Error("No Emulator Bin"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.activeGame = {
|
this.activeGame = {
|
||||||
process: game,
|
process: game,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
[ControlMapping]
|
||||||
|
Up = 10-19
|
||||||
|
Down = 10-20
|
||||||
|
Left = 10-21
|
||||||
|
Right = 10-22
|
||||||
|
Circle = 10-190
|
||||||
|
Cross = 10-189
|
||||||
|
Square = 10-191
|
||||||
|
Triangle = 10-188
|
||||||
|
Start = 10-197
|
||||||
|
Select = 10-196
|
||||||
|
L = 10-193
|
||||||
|
R = 10-192
|
||||||
|
An.Up = 10-4003
|
||||||
|
An.Down = 10-4002
|
||||||
|
An.Left = 10-4001
|
||||||
|
An.Right = 10-4000
|
||||||
|
Fast-forward = 1-193:10-4010,1-135
|
||||||
|
Rewind = 10-196:10-4008
|
||||||
|
Save State = 10-196:10-192,1-132
|
||||||
|
Load State = 10-196:10-193,1-133
|
||||||
|
Previous Slot = 10-197:10-193,1-137
|
||||||
|
Next Slot = 10-197:10-192,1-136
|
||||||
|
Pause = 10-196:10-107,1-111
|
||||||
|
Screenshot = 10-196:10-190
|
||||||
|
Exit App = 10-196:10-197
|
||||||
|
SpeedToggle = 10-196:10-4010
|
||||||
|
|
@ -0,0 +1,479 @@
|
||||||
|
[General]
|
||||||
|
FirstRun = False
|
||||||
|
RunCount = 4
|
||||||
|
Enable Logging = True
|
||||||
|
AutoRun = True
|
||||||
|
Browse = False
|
||||||
|
IgnoreBadMemAccess = True
|
||||||
|
CurrentDirectory = /home
|
||||||
|
ShowDebuggerOnLoad = False
|
||||||
|
CheckForNewVersion = True
|
||||||
|
Language = en_US
|
||||||
|
ForceLagSync2 = False
|
||||||
|
DiscordPresence = True
|
||||||
|
UISound = False
|
||||||
|
AutoLoadSaveState = 0
|
||||||
|
EnableCheats = True
|
||||||
|
CwCheatRefreshRate = 77
|
||||||
|
CwCheatScrollPosition = 0.000000
|
||||||
|
GameListScrollPosition = 0.000000
|
||||||
|
ScreenshotsAsPNG = False
|
||||||
|
UseFFV1 = False
|
||||||
|
DumpFrames = False
|
||||||
|
DumpVideoOutput = False
|
||||||
|
DumpAudio = False
|
||||||
|
SaveLoadResetsAVdumping = False
|
||||||
|
StateSlot = 0
|
||||||
|
EnableStateUndo = True
|
||||||
|
StateLoadUndoGame = NA
|
||||||
|
StateUndoLastSaveGame = NA
|
||||||
|
StateUndoLastSaveSlot = -5
|
||||||
|
RewindFlipFrequency = 0
|
||||||
|
ShowOnScreenMessage = True
|
||||||
|
ShowRegionOnGameIcon = False
|
||||||
|
ShowIDOnGameIcon = True
|
||||||
|
GameGridScale = 1.000000
|
||||||
|
GridView1 = True
|
||||||
|
GridView2 = False
|
||||||
|
GridView3 = False
|
||||||
|
RightAnalogUp = 0
|
||||||
|
RightAnalogDown = 0
|
||||||
|
RightAnalogLeft = 0
|
||||||
|
RightAnalogRight = 0
|
||||||
|
RightAnalogPress = 0
|
||||||
|
RightAnalogCustom = False
|
||||||
|
RightAnalogDisableDiagonal = False
|
||||||
|
SwipeUp = 0
|
||||||
|
SwipeDown = 0
|
||||||
|
SwipeLeft = 0
|
||||||
|
SwipeRight = 0
|
||||||
|
SwipeSensitivity = 1.000000
|
||||||
|
SwipeSmoothing = 0.300000
|
||||||
|
DoubleTapGesture = 0
|
||||||
|
GestureControlEnabled = False
|
||||||
|
ReportingHost = default
|
||||||
|
AutoSaveSymbolMap = False
|
||||||
|
CacheFullIsoInRam = False
|
||||||
|
RemoteISOPort = 0
|
||||||
|
LastRemoteISOServer =
|
||||||
|
LastRemoteISOPort = 0
|
||||||
|
RemoteISOManualConfig = False
|
||||||
|
RemoteShareOnStartup = False
|
||||||
|
RemoteISOSubdir = /
|
||||||
|
RemoteDebuggerOnStartup = False
|
||||||
|
InternalScreenRotation = 1
|
||||||
|
BackgroundAnimation = 1
|
||||||
|
PauseWhenMinimized = False
|
||||||
|
DumpDecryptedEboots = False
|
||||||
|
MemStickInserted = True
|
||||||
|
EnablePlugins = True
|
||||||
|
[CPU]
|
||||||
|
CPUCore = 1
|
||||||
|
SeparateSASThread = True
|
||||||
|
SeparateIOThread = True
|
||||||
|
IOTimingMethod = 0
|
||||||
|
FastMemoryAccess = True
|
||||||
|
FunctionReplacements = True
|
||||||
|
HideSlowWarnings = False
|
||||||
|
HideStateWarnings = False
|
||||||
|
PreloadFunctions = False
|
||||||
|
JitDisableFlags = 0x00000000
|
||||||
|
CPUSpeed = 0
|
||||||
|
[Graphics]
|
||||||
|
EnableCardboardVR = False
|
||||||
|
CardboardScreenSize = 50
|
||||||
|
CardboardXShift = 0
|
||||||
|
CardboardYShift = 0
|
||||||
|
ShowFPSCounter = 0
|
||||||
|
GraphicsBackend = 3 (VULKAN)
|
||||||
|
FailedGraphicsBackends =
|
||||||
|
DisabledGraphicsBackends =
|
||||||
|
VulkanDevice =
|
||||||
|
CameraDevice =
|
||||||
|
RenderingMode = 1
|
||||||
|
SoftwareRenderer = False
|
||||||
|
HardwareTransform = True
|
||||||
|
SoftwareSkinning = True
|
||||||
|
TextureFiltering = 1
|
||||||
|
BufferFiltering = 1
|
||||||
|
InternalResolution = 3
|
||||||
|
AndroidHwScale = 1
|
||||||
|
HighQualityDepth = 1
|
||||||
|
FrameSkip = 0
|
||||||
|
FrameSkipType = 0
|
||||||
|
AutoFrameSkip = False
|
||||||
|
FrameRate = 0
|
||||||
|
FrameRate2 = -1
|
||||||
|
UnthrottlingMode = CONTINUOUS
|
||||||
|
AnisotropyLevel = 4
|
||||||
|
VertexDecCache = False
|
||||||
|
TextureBackoffCache = False
|
||||||
|
TextureSecondaryCache = False
|
||||||
|
FullScreen = True
|
||||||
|
FullScreenMulti = False
|
||||||
|
SmallDisplayZoomType = 2
|
||||||
|
SmallDisplayOffsetX = 0.500000
|
||||||
|
SmallDisplayOffsetY = 0.500000
|
||||||
|
SmallDisplayZoomLevel = 1.000000
|
||||||
|
ImmersiveMode = True
|
||||||
|
SustainedPerformanceMode = False
|
||||||
|
IgnoreScreenInsets = True
|
||||||
|
ReplaceTextures = True
|
||||||
|
SaveNewTextures = False
|
||||||
|
IgnoreTextureFilenames = False
|
||||||
|
TexScalingLevel = 1
|
||||||
|
TexScalingType = 0
|
||||||
|
TexDeposterize = False
|
||||||
|
TexHardwareScaling = False
|
||||||
|
VSyncInterval = False
|
||||||
|
BloomHack = 0
|
||||||
|
SplineBezierQuality = 2
|
||||||
|
HardwareTessellation = False
|
||||||
|
TextureShader = Off
|
||||||
|
ShaderChainRequires60FPS = False
|
||||||
|
MemBlockTransferGPU = True
|
||||||
|
DisableSlowFramebufEffects = False
|
||||||
|
FragmentTestCache = True
|
||||||
|
LogFrameDrops = False
|
||||||
|
InflightFrames = 2
|
||||||
|
RenderDuplicateFrames = False
|
||||||
|
[Sound]
|
||||||
|
Enable = True
|
||||||
|
AudioBackend = 0
|
||||||
|
ExtraAudioBuffering = False
|
||||||
|
GlobalVolume = 10
|
||||||
|
ReverbVolume = 10
|
||||||
|
AltSpeedVolume = -1
|
||||||
|
AudioDevice =
|
||||||
|
AutoAudioDevice = False
|
||||||
|
[Control]
|
||||||
|
HapticFeedback = False
|
||||||
|
ShowTouchCross = True
|
||||||
|
ShowTouchCircle = True
|
||||||
|
ShowTouchSquare = True
|
||||||
|
ShowTouchTriangle = True
|
||||||
|
Custom0Mapping = 0x0000000000000000
|
||||||
|
Custom0Image = 0
|
||||||
|
Custom0Shape = 0
|
||||||
|
Custom0Toggle = False
|
||||||
|
Custom1Mapping = 0x0000000000000000
|
||||||
|
Custom1Image = 1
|
||||||
|
Custom1Shape = 0
|
||||||
|
Custom1Toggle = False
|
||||||
|
Custom2Mapping = 0x0000000000000000
|
||||||
|
Custom2Image = 2
|
||||||
|
Custom2Shape = 0
|
||||||
|
Custom2Toggle = False
|
||||||
|
Custom3Mapping = 0x0000000000000000
|
||||||
|
Custom3Image = 3
|
||||||
|
Custom3Shape = 0
|
||||||
|
Custom3Toggle = False
|
||||||
|
Custom4Mapping = 0x0000000000000000
|
||||||
|
Custom4Image = 4
|
||||||
|
Custom4Shape = 0
|
||||||
|
Custom4Toggle = False
|
||||||
|
Custom5Mapping = 0x0000000000000000
|
||||||
|
Custom5Image = 0
|
||||||
|
Custom5Shape = 1
|
||||||
|
Custom5Toggle = False
|
||||||
|
Custom6Mapping = 0x0000000000000000
|
||||||
|
Custom6Image = 1
|
||||||
|
Custom6Shape = 1
|
||||||
|
Custom6Toggle = False
|
||||||
|
Custom7Mapping = 0x0000000000000000
|
||||||
|
Custom7Image = 2
|
||||||
|
Custom7Shape = 1
|
||||||
|
Custom7Toggle = False
|
||||||
|
Custom8Mapping = 0x0000000000000000
|
||||||
|
Custom8Image = 3
|
||||||
|
Custom8Shape = 1
|
||||||
|
Custom8Toggle = False
|
||||||
|
Custom9Mapping = 0x0000000000000000
|
||||||
|
Custom9Image = 4
|
||||||
|
Custom9Shape = 1
|
||||||
|
Custom9Toggle = False
|
||||||
|
ShowTouchPause = False
|
||||||
|
ShowTouchControls = False
|
||||||
|
DisableDpadDiagonals = False
|
||||||
|
GamepadOnlyFocused = False
|
||||||
|
TouchButtonStyle = 1
|
||||||
|
TouchButtonOpacity = 65
|
||||||
|
TouchButtonHideSeconds = 20
|
||||||
|
AutoCenterTouchAnalog = False
|
||||||
|
AnalogAutoRotSpeed = 8.000000
|
||||||
|
TouchSnapToGrid = False
|
||||||
|
TouchSnapGridSize = 64
|
||||||
|
ActionButtonSpacing2 = 1.000000
|
||||||
|
ActionButtonCenterX = -1.000000
|
||||||
|
ActionButtonCenterY = -1.000000
|
||||||
|
ActionButtonScale = 1.150000
|
||||||
|
DPadX = -1.000000
|
||||||
|
DPadY = -1.000000
|
||||||
|
DPadScale = 1.150000
|
||||||
|
ShowTouchDpad = True
|
||||||
|
DPadSpacing = 1.000000
|
||||||
|
StartKeyX = -1.000000
|
||||||
|
StartKeyY = -1.000000
|
||||||
|
StartKeyScale = 1.150000
|
||||||
|
ShowTouchStart = True
|
||||||
|
SelectKeyX = -1.000000
|
||||||
|
SelectKeyY = -1.000000
|
||||||
|
SelectKeyScale = 1.150000
|
||||||
|
ShowTouchSelect = True
|
||||||
|
UnthrottleKeyX = -1.000000
|
||||||
|
UnthrottleKeyY = -1.000000
|
||||||
|
UnthrottleKeyScale = 1.150000
|
||||||
|
ShowTouchUnthrottle = True
|
||||||
|
LKeyX = -1.000000
|
||||||
|
LKeyY = -1.000000
|
||||||
|
LKeyScale = 1.150000
|
||||||
|
ShowTouchLTrigger = True
|
||||||
|
RKeyX = -1.000000
|
||||||
|
RKeyY = -1.000000
|
||||||
|
RKeyScale = 1.150000
|
||||||
|
ShowTouchRTrigger = True
|
||||||
|
AnalogStickX = -1.000000
|
||||||
|
AnalogStickY = -1.000000
|
||||||
|
AnalogStickScale = 1.150000
|
||||||
|
ShowAnalogStick = True
|
||||||
|
RightAnalogStickX = -1.000000
|
||||||
|
RightAnalogStickY = -1.000000
|
||||||
|
RightAnalogStickScale = 1.150000
|
||||||
|
ShowRightAnalogStick = False
|
||||||
|
fcombo0X = -1.000000
|
||||||
|
fcombo0Y = -1.000000
|
||||||
|
comboKeyScale0 = 1.150000
|
||||||
|
ShowComboKey0 = False
|
||||||
|
fcombo1X = -1.000000
|
||||||
|
fcombo1Y = -1.000000
|
||||||
|
comboKeyScale1 = 1.150000
|
||||||
|
ShowComboKey1 = False
|
||||||
|
fcombo2X = -1.000000
|
||||||
|
fcombo2Y = -1.000000
|
||||||
|
comboKeyScale2 = 1.150000
|
||||||
|
ShowComboKey2 = False
|
||||||
|
fcombo3X = -1.000000
|
||||||
|
fcombo3Y = -1.000000
|
||||||
|
comboKeyScale3 = 1.150000
|
||||||
|
ShowComboKey3 = False
|
||||||
|
fcombo4X = -1.000000
|
||||||
|
fcombo4Y = -1.000000
|
||||||
|
comboKeyScale4 = 1.150000
|
||||||
|
ShowComboKey4 = False
|
||||||
|
fcombo5X = -1.000000
|
||||||
|
fcombo5Y = -1.000000
|
||||||
|
comboKeyScale5 = 1.150000
|
||||||
|
ShowComboKey5 = False
|
||||||
|
fcombo6X = -1.000000
|
||||||
|
fcombo6Y = -1.000000
|
||||||
|
comboKeyScale6 = 1.150000
|
||||||
|
ShowComboKey6 = False
|
||||||
|
fcombo7X = -1.000000
|
||||||
|
fcombo7Y = -1.000000
|
||||||
|
comboKeyScale7 = 1.150000
|
||||||
|
ShowComboKey7 = False
|
||||||
|
fcombo8X = -1.000000
|
||||||
|
fcombo8Y = -1.000000
|
||||||
|
comboKeyScale8 = 1.150000
|
||||||
|
ShowComboKey8 = False
|
||||||
|
fcombo9X = -1.000000
|
||||||
|
fcombo9Y = -1.000000
|
||||||
|
comboKeyScale9 = 1.150000
|
||||||
|
ShowComboKey9 = False
|
||||||
|
AnalogDeadzone = 0.150000
|
||||||
|
AnalogInverseDeadzone = 0.000000
|
||||||
|
AnalogSensitivity = 1.100000
|
||||||
|
AnalogIsCircular = False
|
||||||
|
AnalogLimiterDeadzone = 0.600000
|
||||||
|
LeftStickHeadScale = 1.000000
|
||||||
|
RightStickHeadScale = 1.000000
|
||||||
|
HideStickBackground = False
|
||||||
|
UseMouse = False
|
||||||
|
MapMouse = False
|
||||||
|
ConfineMap = False
|
||||||
|
MouseSensitivity = 0.100000
|
||||||
|
MouseSmoothing = 0.900000
|
||||||
|
SystemControls = True
|
||||||
|
AllowMappingCombos = True
|
||||||
|
[Network]
|
||||||
|
EnableWlan = False
|
||||||
|
EnableAdhocServer = False
|
||||||
|
proAdhocServer = socom.cc
|
||||||
|
PortOffset = 10000
|
||||||
|
MinTimeout = 0
|
||||||
|
ForcedFirstConnect = False
|
||||||
|
EnableUPnP = False
|
||||||
|
UPnPUseOriginalPort = False
|
||||||
|
EnableNetworkChat = False
|
||||||
|
ChatButtonPosition = 0
|
||||||
|
ChatScreenPosition = 0
|
||||||
|
EnableQuickChat = True
|
||||||
|
QuickChat1 = Quick Chat 1
|
||||||
|
QuickChat2 = Quick Chat 2
|
||||||
|
QuickChat3 = Quick Chat 3
|
||||||
|
QuickChat4 = Quick Chat 4
|
||||||
|
QuickChat5 = Quick Chat 5
|
||||||
|
[SystemParam]
|
||||||
|
PSPModel = 1
|
||||||
|
PSPFirmwareVersion = 660
|
||||||
|
NickName = PPSSPP
|
||||||
|
MacAddress = ec:fd:62:d4:ec:73
|
||||||
|
Language = 1
|
||||||
|
ParamTimeFormat = 0
|
||||||
|
ParamDateFormat = 0
|
||||||
|
TimeZone = 0
|
||||||
|
DayLightSavings = False
|
||||||
|
ButtonPreference = 1
|
||||||
|
LockParentalLevel = 0
|
||||||
|
WlanAdhocChannel = 0
|
||||||
|
WlanPowerSave = False
|
||||||
|
EncryptSave = True
|
||||||
|
SavedataUpgradeVersion = True
|
||||||
|
MemStickSize = 16
|
||||||
|
[Debugger]
|
||||||
|
DisasmWindowX = -1
|
||||||
|
DisasmWindowY = -1
|
||||||
|
DisasmWindowW = -1
|
||||||
|
DisasmWindowH = -1
|
||||||
|
GEWindowX = -1
|
||||||
|
GEWindowY = -1
|
||||||
|
GEWindowW = -1
|
||||||
|
GEWindowH = -1
|
||||||
|
ConsoleWindowX = -1
|
||||||
|
ConsoleWindowY = -1
|
||||||
|
FontWidth = 8
|
||||||
|
FontHeight = 12
|
||||||
|
DisplayStatusBar = True
|
||||||
|
ShowBottomTabTitles = True
|
||||||
|
ShowDeveloperMenu = False
|
||||||
|
SkipDeadbeefFilling = False
|
||||||
|
FuncHashMap = False
|
||||||
|
MemInfoDetailed = False
|
||||||
|
DrawFrameGraph = False
|
||||||
|
[Upgrade]
|
||||||
|
UpgradeMessage =
|
||||||
|
UpgradeVersion =
|
||||||
|
DismissedVersion =
|
||||||
|
[Theme]
|
||||||
|
ItemStyleFg = 0xffffffff
|
||||||
|
ItemStyleBg = 0x55000000
|
||||||
|
ItemFocusedStyleFg = 0xffffffff
|
||||||
|
ItemFocusedStyleBg = 0xffedc24c
|
||||||
|
ItemDownStyleFg = 0xffffffff
|
||||||
|
ItemDownStyleBg = 0xffbd9939
|
||||||
|
ItemDisabledStyleFg = 0x80eeeeee
|
||||||
|
ItemDisabledStyleBg = 0x55e0d4af
|
||||||
|
ItemHighlightedStyleFg = 0xffffffff
|
||||||
|
ItemHighlightedStyleBg = 0x55bdbb39
|
||||||
|
ButtonStyleFg = 0xffffffff
|
||||||
|
ButtonStyleBg = 0x55000000
|
||||||
|
ButtonFocusedStyleFg = 0xffffffff
|
||||||
|
ButtonFocusedStyleBg = 0xffedc24c
|
||||||
|
ButtonDownStyleFg = 0xffffffff
|
||||||
|
ButtonDownStyleBg = 0xffbd9939
|
||||||
|
ButtonDisabledStyleFg = 0x80eeeeee
|
||||||
|
ButtonDisabledStyleBg = 0x55e0d4af
|
||||||
|
ButtonHighlightedStyleFg = 0xffffffff
|
||||||
|
ButtonHighlightedStyleBg = 0x55bdbb39
|
||||||
|
HeaderStyleFg = 0xffffffff
|
||||||
|
InfoStyleFg = 0xffffffff
|
||||||
|
InfoStyleBg = 0x00000000
|
||||||
|
PopupTitleStyleFg = 0xffe3be59
|
||||||
|
PopupStyleFg = 0xffffffff
|
||||||
|
PopupStyleBg = 0xff303030
|
||||||
|
[Recent]
|
||||||
|
MaxRecent = 60
|
||||||
|
[Log]
|
||||||
|
SYSTEMEnabled = True
|
||||||
|
SYSTEMLevel = 2
|
||||||
|
BOOTEnabled = True
|
||||||
|
BOOTLevel = 2
|
||||||
|
COMMONEnabled = True
|
||||||
|
COMMONLevel = 2
|
||||||
|
CPUEnabled = True
|
||||||
|
CPULevel = 2
|
||||||
|
FILESYSEnabled = True
|
||||||
|
FILESYSLevel = 2
|
||||||
|
G3DEnabled = True
|
||||||
|
G3DLevel = 2
|
||||||
|
HLEEnabled = True
|
||||||
|
HLELevel = 2
|
||||||
|
JITEnabled = True
|
||||||
|
JITLevel = 2
|
||||||
|
LOADEREnabled = True
|
||||||
|
LOADERLevel = 2
|
||||||
|
MEEnabled = True
|
||||||
|
MELevel = 2
|
||||||
|
MEMMAPEnabled = True
|
||||||
|
MEMMAPLevel = 2
|
||||||
|
SASMIXEnabled = True
|
||||||
|
SASMIXLevel = 2
|
||||||
|
SAVESTATEEnabled = True
|
||||||
|
SAVESTATELevel = 2
|
||||||
|
FRAMEBUFEnabled = True
|
||||||
|
FRAMEBUFLevel = 2
|
||||||
|
AUDIOEnabled = True
|
||||||
|
AUDIOLevel = 2
|
||||||
|
IOEnabled = True
|
||||||
|
IOLevel = 2
|
||||||
|
SCEAUDIOEnabled = True
|
||||||
|
SCEAUDIOLevel = 2
|
||||||
|
SCECTRLEnabled = True
|
||||||
|
SCECTRLLevel = 2
|
||||||
|
SCEDISPEnabled = True
|
||||||
|
SCEDISPLevel = 2
|
||||||
|
SCEFONTEnabled = True
|
||||||
|
SCEFONTLevel = 2
|
||||||
|
SCEGEEnabled = True
|
||||||
|
SCEGELevel = 2
|
||||||
|
SCEINTCEnabled = True
|
||||||
|
SCEINTCLevel = 2
|
||||||
|
SCEIOEnabled = True
|
||||||
|
SCEIOLevel = 2
|
||||||
|
SCEKERNELEnabled = True
|
||||||
|
SCEKERNELLevel = 2
|
||||||
|
SCEMODULEEnabled = True
|
||||||
|
SCEMODULELevel = 2
|
||||||
|
SCENETEnabled = True
|
||||||
|
SCENETLevel = 2
|
||||||
|
SCERTCEnabled = True
|
||||||
|
SCERTCLevel = 2
|
||||||
|
SCESASEnabled = True
|
||||||
|
SCESASLevel = 2
|
||||||
|
SCEUTILEnabled = True
|
||||||
|
SCEUTILLevel = 2
|
||||||
|
SCEMISCEnabled = True
|
||||||
|
SCEMISCLevel = 2
|
||||||
|
ACHIEVEMENTSEnabled = True
|
||||||
|
ACHIEVEMENTSLevel = 2
|
||||||
|
HTTPEnabled = True
|
||||||
|
HTTPLevel = 2
|
||||||
|
PRINTFEnabled = True
|
||||||
|
PRINTFLevel = 2
|
||||||
|
[PostShaderSetting]
|
||||||
|
BloomSettingValue1 = 0.600000
|
||||||
|
BloomSettingValue2 = 0.500000
|
||||||
|
CartoonSettingValue1 = 0.500000
|
||||||
|
ColorCorrectionSettingValue1 = 1.000000
|
||||||
|
ColorCorrectionSettingValue2 = 1.000000
|
||||||
|
ColorCorrectionSettingValue3 = 1.000000
|
||||||
|
ColorCorrectionSettingValue4 = 1.000000
|
||||||
|
ScanlinesSettingValue1 = 1.000000
|
||||||
|
ScanlinesSettingValue2 = 0.500000
|
||||||
|
SharpenSettingValue1 = 1.500000
|
||||||
|
[Achievements]
|
||||||
|
AchievementsEnable = False
|
||||||
|
AchievementsChallengeMode = False
|
||||||
|
AchievementsEncoreMode = False
|
||||||
|
AchievementsUnofficial = False
|
||||||
|
AchievementsLogBadMemReads = False
|
||||||
|
AchievementsUserName =
|
||||||
|
AchievementsSoundEffects = True
|
||||||
|
AchievementsUnlockAudioFile =
|
||||||
|
AchievementsLeaderboardSubmitAudioFile =
|
||||||
|
AchievementsLeaderboardTrackerPos = 3
|
||||||
|
AchievementsLeaderboardStartedOrFailedPos = 3
|
||||||
|
AchievementsLeaderboardSubmittedPos = 3
|
||||||
|
AchievementsProgressPos = 3
|
||||||
|
AchievementsChallengePos = 3
|
||||||
|
AchievementsUnlockedPos = 4
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"name": "com.simeonradivoev.gameflow.ppsspp",
|
||||||
|
"displayName": "PPSSPP Integration",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "PPSSPP Emulator Integration",
|
||||||
|
"main": "./ppsspp.ts",
|
||||||
|
"icon": "https://www.ppsspp.org/static/img/platform/ppsspp-icon.png",
|
||||||
|
"keywords": [
|
||||||
|
"integration",
|
||||||
|
"emulator",
|
||||||
|
"psp",
|
||||||
|
"ppsspp"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { PluginContextType, PluginType } from "@/bun/types/typesc.schema";
|
||||||
|
import desc from './package.json';
|
||||||
|
import { config } from "@/bun/api/app";
|
||||||
|
import configFilePathWin32 from './win32/ppsspp.ini' with { type: 'file' };
|
||||||
|
import configControlsFilePathWin32 from './win32/controls.ini' with { type: 'file' };
|
||||||
|
import configFilePathLinux from './linux/ppsspp.ini' with { type: 'file' };
|
||||||
|
import configControlsFilePathLinux from './linux/controls.ini' with { type: 'file' };
|
||||||
|
import path from "node:path";
|
||||||
|
import Mustache from "mustache";
|
||||||
|
import { ensureDir } from "fs-extra";
|
||||||
|
|
||||||
|
export default class PCSX2Integration implements PluginType
|
||||||
|
{
|
||||||
|
load (ctx: PluginContextType)
|
||||||
|
{
|
||||||
|
ctx.hooks.games.emulatorLaunch.tapPromise(desc.name, async (ctx) =>
|
||||||
|
{
|
||||||
|
if (ctx.autoValidCommand.emulator === 'PPSSPP' && ctx.autoValidCommand.emulatorSource === 'store' && ctx.autoValidCommand.metadata.emulatorDir)
|
||||||
|
{
|
||||||
|
const args = [ctx.autoValidCommand.metadata.romPath, "--escape-exit", "--pause-menu-exit"];
|
||||||
|
if (config.get('launchInFullscreen'))
|
||||||
|
{
|
||||||
|
args.push("--fullscreen");
|
||||||
|
}
|
||||||
|
|
||||||
|
let confPath: string | undefined = undefined;
|
||||||
|
let controlsPath: string | undefined = undefined;
|
||||||
|
|
||||||
|
switch (process.platform)
|
||||||
|
{
|
||||||
|
case "win32":
|
||||||
|
confPath = configFilePathWin32;
|
||||||
|
controlsPath = configControlsFilePathWin32;
|
||||||
|
break;
|
||||||
|
case 'linux':
|
||||||
|
confPath = configFilePathLinux;
|
||||||
|
controlsPath = configControlsFilePathLinux;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (controlsPath)
|
||||||
|
{
|
||||||
|
const configFileContents = await Bun.file(controlsPath).text();
|
||||||
|
const controlsFileContents = await Bun.file(controlsPath).text();
|
||||||
|
ensureDir(path.join(ctx.autoValidCommand.metadata.emulatorDir, 'memstick', 'PSP', 'SYSTEM'));
|
||||||
|
await Bun.write(path.join(ctx.autoValidCommand.metadata.emulatorDir, 'memstick', 'PSP', 'SYSTEM', 'ppsspp.ini'), Mustache.render(configFileContents, {}));
|
||||||
|
await Bun.write(path.join(ctx.autoValidCommand.metadata.emulatorDir, 'memstick', 'PSP', 'SYSTEM', 'controls.ini'), Mustache.render(controlsFileContents, {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
[ControlMapping]
|
||||||
|
Up = 20-19
|
||||||
|
Down = 20-20
|
||||||
|
Left = 20-21
|
||||||
|
Right = 20-22
|
||||||
|
Circle = 20-97
|
||||||
|
Cross = 20-96
|
||||||
|
Square = 20-100
|
||||||
|
Triangle = 20-99
|
||||||
|
Start = 20-108
|
||||||
|
Select = 20-109
|
||||||
|
L = 20-102
|
||||||
|
R = 20-103
|
||||||
|
An.Up = 20-4002
|
||||||
|
An.Down = 20-4003
|
||||||
|
An.Left = 20-4001
|
||||||
|
An.Right = 20-4000
|
||||||
|
Fast-forward = 20-109:20-4036
|
||||||
|
Rewind = 20-109:20-4034
|
||||||
|
Save State = 20-109:20-103
|
||||||
|
Load State = 20-109:20-102
|
||||||
|
Home = 20-108:20-109
|
||||||
|
Exit App = 20-3:20-108
|
||||||
|
|
@ -0,0 +1,472 @@
|
||||||
|
[General]
|
||||||
|
FirstRun = False
|
||||||
|
RunCount = 4
|
||||||
|
Enable Logging = True
|
||||||
|
AutoRun = True
|
||||||
|
Browse = False
|
||||||
|
IgnoreBadMemAccess = True
|
||||||
|
CurrentDirectory = C:/Emulation/roms/psp
|
||||||
|
ShowDebuggerOnLoad = False
|
||||||
|
CheckForNewVersion = True
|
||||||
|
Language = en_US
|
||||||
|
ForceLagSync2 = False
|
||||||
|
DiscordPresence = True
|
||||||
|
UISound = False
|
||||||
|
AutoLoadSaveState = 0
|
||||||
|
EnableCheats = True
|
||||||
|
CwCheatRefreshRate = 77
|
||||||
|
CwCheatScrollPosition = 0.000000
|
||||||
|
GameListScrollPosition = 0.000000
|
||||||
|
ScreenshotsAsPNG = False
|
||||||
|
UseFFV1 = False
|
||||||
|
DumpFrames = False
|
||||||
|
DumpVideoOutput = False
|
||||||
|
DumpAudio = False
|
||||||
|
SaveLoadResetsAVdumping = False
|
||||||
|
StateSlot = 0
|
||||||
|
EnableStateUndo = True
|
||||||
|
StateLoadUndoGame = NA
|
||||||
|
StateUndoLastSaveGame = NA
|
||||||
|
StateUndoLastSaveSlot = -5
|
||||||
|
RewindFlipFrequency = 0
|
||||||
|
ShowOnScreenMessage = True
|
||||||
|
ShowRegionOnGameIcon = False
|
||||||
|
ShowIDOnGameIcon = False
|
||||||
|
GameGridScale = 1.000000
|
||||||
|
GridView1 = True
|
||||||
|
GridView2 = True
|
||||||
|
GridView3 = False
|
||||||
|
RightAnalogUp = 0
|
||||||
|
RightAnalogDown = 0
|
||||||
|
RightAnalogLeft = 0
|
||||||
|
RightAnalogRight = 0
|
||||||
|
RightAnalogPress = 0
|
||||||
|
RightAnalogCustom = False
|
||||||
|
RightAnalogDisableDiagonal = False
|
||||||
|
SwipeUp = 0
|
||||||
|
SwipeDown = 0
|
||||||
|
SwipeLeft = 0
|
||||||
|
SwipeRight = 0
|
||||||
|
SwipeSensitivity = 1.000000
|
||||||
|
SwipeSmoothing = 0.300000
|
||||||
|
DoubleTapGesture = 0
|
||||||
|
GestureControlEnabled = False
|
||||||
|
ReportingHost = default
|
||||||
|
AutoSaveSymbolMap = False
|
||||||
|
CacheFullIsoInRam = False
|
||||||
|
RemoteISOPort = 0
|
||||||
|
LastRemoteISOServer =
|
||||||
|
LastRemoteISOPort = 0
|
||||||
|
RemoteISOManualConfig = False
|
||||||
|
RemoteShareOnStartup = False
|
||||||
|
RemoteISOSubdir = /
|
||||||
|
RemoteDebuggerOnStartup = False
|
||||||
|
InternalScreenRotation = 1
|
||||||
|
BackgroundAnimation = 1
|
||||||
|
PauseWhenMinimized = False
|
||||||
|
DumpDecryptedEboots = False
|
||||||
|
MemStickInserted = True
|
||||||
|
EnablePlugins = True
|
||||||
|
[CPU]
|
||||||
|
CPUCore = 1
|
||||||
|
SeparateSASThread = True
|
||||||
|
SeparateIOThread = True
|
||||||
|
IOTimingMethod = 0
|
||||||
|
FastMemoryAccess = True
|
||||||
|
FunctionReplacements = True
|
||||||
|
HideSlowWarnings = False
|
||||||
|
HideStateWarnings = False
|
||||||
|
PreloadFunctions = False
|
||||||
|
JitDisableFlags = 0x00000000
|
||||||
|
CPUSpeed = 0
|
||||||
|
[Graphics]
|
||||||
|
EnableCardboardVR = False
|
||||||
|
CardboardScreenSize = 50
|
||||||
|
CardboardXShift = 0
|
||||||
|
CardboardYShift = 0
|
||||||
|
ShowFPSCounter = 0
|
||||||
|
GraphicsBackend = 3 (VULKAN)
|
||||||
|
FailedGraphicsBackends =
|
||||||
|
DisabledGraphicsBackends =
|
||||||
|
VulkanDevice =
|
||||||
|
CameraDevice =
|
||||||
|
RenderingMode = 1
|
||||||
|
SoftwareRenderer = False
|
||||||
|
HardwareTransform = True
|
||||||
|
SoftwareSkinning = True
|
||||||
|
TextureFiltering = 1
|
||||||
|
BufferFiltering = 1
|
||||||
|
InternalResolution = 3
|
||||||
|
AndroidHwScale = 1
|
||||||
|
HighQualityDepth = 1
|
||||||
|
FrameSkip = 0
|
||||||
|
FrameSkipType = 0
|
||||||
|
AutoFrameSkip = False
|
||||||
|
FrameRate = 0
|
||||||
|
FrameRate2 = -1
|
||||||
|
UnthrottlingMode = CONTINUOUS
|
||||||
|
AnisotropyLevel = 4
|
||||||
|
VertexDecCache = False
|
||||||
|
TextureBackoffCache = False
|
||||||
|
TextureSecondaryCache = False
|
||||||
|
FullScreen = True
|
||||||
|
FullScreenMulti = False
|
||||||
|
SmallDisplayZoomType = 2
|
||||||
|
SmallDisplayOffsetX = 0.500000
|
||||||
|
SmallDisplayOffsetY = 0.500000
|
||||||
|
SmallDisplayZoomLevel = 1.000000
|
||||||
|
ImmersiveMode = True
|
||||||
|
SustainedPerformanceMode = False
|
||||||
|
IgnoreScreenInsets = True
|
||||||
|
ReplaceTextures = True
|
||||||
|
SaveNewTextures = False
|
||||||
|
IgnoreTextureFilenames = False
|
||||||
|
TexScalingLevel = 1
|
||||||
|
TexScalingType = 0
|
||||||
|
TexDeposterize = False
|
||||||
|
TexHardwareScaling = False
|
||||||
|
VSyncInterval = False
|
||||||
|
BloomHack = 0
|
||||||
|
SplineBezierQuality = 2
|
||||||
|
HardwareTessellation = False
|
||||||
|
TextureShader = Off
|
||||||
|
ShaderChainRequires60FPS = False
|
||||||
|
MemBlockTransferGPU = True
|
||||||
|
DisableSlowFramebufEffects = False
|
||||||
|
FragmentTestCache = True
|
||||||
|
LogFrameDrops = False
|
||||||
|
InflightFrames = 2
|
||||||
|
RenderDuplicateFrames = False
|
||||||
|
[Sound]
|
||||||
|
Enable = True
|
||||||
|
AudioBackend = 0
|
||||||
|
ExtraAudioBuffering = False
|
||||||
|
GlobalVolume = 10
|
||||||
|
ReverbVolume = 10
|
||||||
|
AltSpeedVolume = -1
|
||||||
|
AudioDevice =
|
||||||
|
AutoAudioDevice = False
|
||||||
|
[Control]
|
||||||
|
HapticFeedback = False
|
||||||
|
ShowTouchCross = True
|
||||||
|
ShowTouchCircle = True
|
||||||
|
ShowTouchSquare = True
|
||||||
|
ShowTouchTriangle = True
|
||||||
|
Custom0Mapping = 0x0000000000000000
|
||||||
|
Custom0Image = 0
|
||||||
|
Custom0Shape = 0
|
||||||
|
Custom0Toggle = False
|
||||||
|
Custom1Mapping = 0x0000000000000000
|
||||||
|
Custom1Image = 1
|
||||||
|
Custom1Shape = 0
|
||||||
|
Custom1Toggle = False
|
||||||
|
Custom2Mapping = 0x0000000000000000
|
||||||
|
Custom2Image = 2
|
||||||
|
Custom2Shape = 0
|
||||||
|
Custom2Toggle = False
|
||||||
|
Custom3Mapping = 0x0000000000000000
|
||||||
|
Custom3Image = 3
|
||||||
|
Custom3Shape = 0
|
||||||
|
Custom3Toggle = False
|
||||||
|
Custom4Mapping = 0x0000000000000000
|
||||||
|
Custom4Image = 4
|
||||||
|
Custom4Shape = 0
|
||||||
|
Custom4Toggle = False
|
||||||
|
Custom5Mapping = 0x0000000000000000
|
||||||
|
Custom5Image = 0
|
||||||
|
Custom5Shape = 1
|
||||||
|
Custom5Toggle = False
|
||||||
|
Custom6Mapping = 0x0000000000000000
|
||||||
|
Custom6Image = 1
|
||||||
|
Custom6Shape = 1
|
||||||
|
Custom6Toggle = False
|
||||||
|
Custom7Mapping = 0x0000000000000000
|
||||||
|
Custom7Image = 2
|
||||||
|
Custom7Shape = 1
|
||||||
|
Custom7Toggle = False
|
||||||
|
Custom8Mapping = 0x0000000000000000
|
||||||
|
Custom8Image = 3
|
||||||
|
Custom8Shape = 1
|
||||||
|
Custom8Toggle = False
|
||||||
|
Custom9Mapping = 0x0000000000000000
|
||||||
|
Custom9Image = 4
|
||||||
|
Custom9Shape = 1
|
||||||
|
Custom9Toggle = False
|
||||||
|
ShowTouchPause = False
|
||||||
|
ShowTouchControls = False
|
||||||
|
DisableDpadDiagonals = False
|
||||||
|
GamepadOnlyFocused = False
|
||||||
|
TouchButtonStyle = 1
|
||||||
|
TouchButtonOpacity = 65
|
||||||
|
TouchButtonHideSeconds = 20
|
||||||
|
AutoCenterTouchAnalog = False
|
||||||
|
AnalogAutoRotSpeed = 8.000000
|
||||||
|
TouchSnapToGrid = False
|
||||||
|
TouchSnapGridSize = 64
|
||||||
|
ActionButtonSpacing2 = 1.000000
|
||||||
|
ActionButtonCenterX = -1.000000
|
||||||
|
ActionButtonCenterY = -1.000000
|
||||||
|
ActionButtonScale = 1.150000
|
||||||
|
DPadX = -1.000000
|
||||||
|
DPadY = -1.000000
|
||||||
|
DPadScale = 1.150000
|
||||||
|
ShowTouchDpad = True
|
||||||
|
DPadSpacing = 1.000000
|
||||||
|
StartKeyX = -1.000000
|
||||||
|
StartKeyY = -1.000000
|
||||||
|
StartKeyScale = 1.150000
|
||||||
|
ShowTouchStart = True
|
||||||
|
SelectKeyX = -1.000000
|
||||||
|
SelectKeyY = -1.000000
|
||||||
|
SelectKeyScale = 1.150000
|
||||||
|
ShowTouchSelect = True
|
||||||
|
UnthrottleKeyX = -1.000000
|
||||||
|
UnthrottleKeyY = -1.000000
|
||||||
|
UnthrottleKeyScale = 1.150000
|
||||||
|
ShowTouchUnthrottle = True
|
||||||
|
LKeyX = -1.000000
|
||||||
|
LKeyY = -1.000000
|
||||||
|
LKeyScale = 1.150000
|
||||||
|
ShowTouchLTrigger = True
|
||||||
|
RKeyX = -1.000000
|
||||||
|
RKeyY = -1.000000
|
||||||
|
RKeyScale = 1.150000
|
||||||
|
ShowTouchRTrigger = True
|
||||||
|
AnalogStickX = -1.000000
|
||||||
|
AnalogStickY = -1.000000
|
||||||
|
AnalogStickScale = 1.150000
|
||||||
|
ShowAnalogStick = True
|
||||||
|
RightAnalogStickX = -1.000000
|
||||||
|
RightAnalogStickY = -1.000000
|
||||||
|
RightAnalogStickScale = 1.150000
|
||||||
|
ShowRightAnalogStick = False
|
||||||
|
fcombo0X = -1.000000
|
||||||
|
fcombo0Y = -1.000000
|
||||||
|
comboKeyScale0 = 1.150000
|
||||||
|
ShowComboKey0 = False
|
||||||
|
fcombo1X = -1.000000
|
||||||
|
fcombo1Y = -1.000000
|
||||||
|
comboKeyScale1 = 1.150000
|
||||||
|
ShowComboKey1 = False
|
||||||
|
fcombo2X = -1.000000
|
||||||
|
fcombo2Y = -1.000000
|
||||||
|
comboKeyScale2 = 1.150000
|
||||||
|
ShowComboKey2 = False
|
||||||
|
fcombo3X = -1.000000
|
||||||
|
fcombo3Y = -1.000000
|
||||||
|
comboKeyScale3 = 1.150000
|
||||||
|
ShowComboKey3 = False
|
||||||
|
fcombo4X = -1.000000
|
||||||
|
fcombo4Y = -1.000000
|
||||||
|
comboKeyScale4 = 1.150000
|
||||||
|
ShowComboKey4 = False
|
||||||
|
fcombo5X = -1.000000
|
||||||
|
fcombo5Y = -1.000000
|
||||||
|
comboKeyScale5 = 1.150000
|
||||||
|
ShowComboKey5 = False
|
||||||
|
fcombo6X = -1.000000
|
||||||
|
fcombo6Y = -1.000000
|
||||||
|
comboKeyScale6 = 1.150000
|
||||||
|
ShowComboKey6 = False
|
||||||
|
fcombo7X = -1.000000
|
||||||
|
fcombo7Y = -1.000000
|
||||||
|
comboKeyScale7 = 1.150000
|
||||||
|
ShowComboKey7 = False
|
||||||
|
fcombo8X = -1.000000
|
||||||
|
fcombo8Y = -1.000000
|
||||||
|
comboKeyScale8 = 1.150000
|
||||||
|
ShowComboKey8 = False
|
||||||
|
fcombo9X = -1.000000
|
||||||
|
fcombo9Y = -1.000000
|
||||||
|
comboKeyScale9 = 1.150000
|
||||||
|
ShowComboKey9 = False
|
||||||
|
AnalogDeadzone = 0.150000
|
||||||
|
AnalogInverseDeadzone = 0.000000
|
||||||
|
AnalogSensitivity = 1.100000
|
||||||
|
AnalogIsCircular = False
|
||||||
|
AnalogLimiterDeadzone = 0.600000
|
||||||
|
LeftStickHeadScale = 1.000000
|
||||||
|
RightStickHeadScale = 1.000000
|
||||||
|
HideStickBackground = False
|
||||||
|
UseMouse = False
|
||||||
|
MapMouse = False
|
||||||
|
ConfineMap = False
|
||||||
|
MouseSensitivity = 0.100000
|
||||||
|
MouseSmoothing = 0.900000
|
||||||
|
SystemControls = True
|
||||||
|
AllowMappingCombos = True
|
||||||
|
[Network]
|
||||||
|
EnableWlan = False
|
||||||
|
EnableAdhocServer = False
|
||||||
|
proAdhocServer = socom.cc
|
||||||
|
PortOffset = 10000
|
||||||
|
MinTimeout = 0
|
||||||
|
ForcedFirstConnect = False
|
||||||
|
EnableUPnP = False
|
||||||
|
UPnPUseOriginalPort = False
|
||||||
|
EnableNetworkChat = False
|
||||||
|
ChatButtonPosition = 0
|
||||||
|
ChatScreenPosition = 0
|
||||||
|
EnableQuickChat = True
|
||||||
|
QuickChat1 = Quick Chat 1
|
||||||
|
QuickChat2 = Quick Chat 2
|
||||||
|
QuickChat3 = Quick Chat 3
|
||||||
|
QuickChat4 = Quick Chat 4
|
||||||
|
QuickChat5 = Quick Chat 5
|
||||||
|
[SystemParam]
|
||||||
|
PSPModel = 1
|
||||||
|
PSPFirmwareVersion = 660
|
||||||
|
NickName = PPSSPP
|
||||||
|
MacAddress = ec:fd:62:d4:ec:73
|
||||||
|
Language = 1
|
||||||
|
ParamTimeFormat = 0
|
||||||
|
ParamDateFormat = 0
|
||||||
|
TimeZone = 0
|
||||||
|
DayLightSavings = False
|
||||||
|
ButtonPreference = 1
|
||||||
|
LockParentalLevel = 0
|
||||||
|
WlanAdhocChannel = 0
|
||||||
|
WlanPowerSave = False
|
||||||
|
EncryptSave = True
|
||||||
|
SavedataUpgradeVersion = True
|
||||||
|
MemStickSize = 16
|
||||||
|
[Debugger]
|
||||||
|
DisasmWindowX = -1
|
||||||
|
DisasmWindowY = -1
|
||||||
|
DisasmWindowW = -1
|
||||||
|
DisasmWindowH = -1
|
||||||
|
GEWindowX = -1
|
||||||
|
GEWindowY = -1
|
||||||
|
GEWindowW = -1
|
||||||
|
GEWindowH = -1
|
||||||
|
ConsoleWindowX = -1
|
||||||
|
ConsoleWindowY = -1
|
||||||
|
FontWidth = 8
|
||||||
|
FontHeight = 12
|
||||||
|
DisplayStatusBar = True
|
||||||
|
ShowBottomTabTitles = True
|
||||||
|
ShowDeveloperMenu = False
|
||||||
|
SkipDeadbeefFilling = False
|
||||||
|
FuncHashMap = False
|
||||||
|
MemInfoDetailed = False
|
||||||
|
DrawFrameGraph = False
|
||||||
|
[Upgrade]
|
||||||
|
UpgradeMessage =
|
||||||
|
UpgradeVersion =
|
||||||
|
DismissedVersion =
|
||||||
|
[Theme]
|
||||||
|
ItemStyleFg = 0xffffffff
|
||||||
|
ItemStyleBg = 0x55000000
|
||||||
|
ItemFocusedStyleFg = 0xffffffff
|
||||||
|
ItemFocusedStyleBg = 0xffedc24c
|
||||||
|
ItemDownStyleFg = 0xffffffff
|
||||||
|
ItemDownStyleBg = 0xffbd9939
|
||||||
|
ItemDisabledStyleFg = 0x80eeeeee
|
||||||
|
ItemDisabledStyleBg = 0x55e0d4af
|
||||||
|
ItemHighlightedStyleFg = 0xffffffff
|
||||||
|
ItemHighlightedStyleBg = 0x55bdbb39
|
||||||
|
ButtonStyleFg = 0xffffffff
|
||||||
|
ButtonStyleBg = 0x55000000
|
||||||
|
ButtonFocusedStyleFg = 0xffffffff
|
||||||
|
ButtonFocusedStyleBg = 0xffedc24c
|
||||||
|
ButtonDownStyleFg = 0xffffffff
|
||||||
|
ButtonDownStyleBg = 0xffbd9939
|
||||||
|
ButtonDisabledStyleFg = 0x80eeeeee
|
||||||
|
ButtonDisabledStyleBg = 0x55e0d4af
|
||||||
|
ButtonHighlightedStyleFg = 0xffffffff
|
||||||
|
ButtonHighlightedStyleBg = 0x55bdbb39
|
||||||
|
HeaderStyleFg = 0xffffffff
|
||||||
|
InfoStyleFg = 0xffffffff
|
||||||
|
InfoStyleBg = 0x00000000
|
||||||
|
PopupTitleStyleFg = 0xffe3be59
|
||||||
|
PopupStyleFg = 0xffffffff
|
||||||
|
PopupStyleBg = 0xff303030
|
||||||
|
[Recent]
|
||||||
|
MaxRecent = 60
|
||||||
|
[Log]
|
||||||
|
SYSTEMEnabled = True
|
||||||
|
SYSTEMLevel = 2
|
||||||
|
BOOTEnabled = True
|
||||||
|
BOOTLevel = 2
|
||||||
|
COMMONEnabled = True
|
||||||
|
COMMONLevel = 2
|
||||||
|
CPUEnabled = True
|
||||||
|
CPULevel = 2
|
||||||
|
FILESYSEnabled = True
|
||||||
|
FILESYSLevel = 2
|
||||||
|
G3DEnabled = True
|
||||||
|
G3DLevel = 2
|
||||||
|
HLEEnabled = True
|
||||||
|
HLELevel = 2
|
||||||
|
JITEnabled = True
|
||||||
|
JITLevel = 2
|
||||||
|
LOADEREnabled = True
|
||||||
|
LOADERLevel = 2
|
||||||
|
MEEnabled = True
|
||||||
|
MELevel = 2
|
||||||
|
MEMMAPEnabled = True
|
||||||
|
MEMMAPLevel = 2
|
||||||
|
SASMIXEnabled = True
|
||||||
|
SASMIXLevel = 2
|
||||||
|
SAVESTATEEnabled = True
|
||||||
|
SAVESTATELevel = 2
|
||||||
|
FRAMEBUFEnabled = True
|
||||||
|
FRAMEBUFLevel = 2
|
||||||
|
AUDIOEnabled = True
|
||||||
|
AUDIOLevel = 2
|
||||||
|
IOEnabled = True
|
||||||
|
IOLevel = 2
|
||||||
|
SCEAUDIOEnabled = True
|
||||||
|
SCEAUDIOLevel = 2
|
||||||
|
SCECTRLEnabled = True
|
||||||
|
SCECTRLLevel = 2
|
||||||
|
SCEDISPEnabled = True
|
||||||
|
SCEDISPLevel = 2
|
||||||
|
SCEFONTEnabled = True
|
||||||
|
SCEFONTLevel = 2
|
||||||
|
SCEGEEnabled = True
|
||||||
|
SCEGELevel = 2
|
||||||
|
SCEINTCEnabled = True
|
||||||
|
SCEINTCLevel = 2
|
||||||
|
SCEIOEnabled = True
|
||||||
|
SCEIOLevel = 2
|
||||||
|
SCEKERNELEnabled = True
|
||||||
|
SCEKERNELLevel = 2
|
||||||
|
SCEMODULEEnabled = True
|
||||||
|
SCEMODULELevel = 2
|
||||||
|
SCENETEnabled = True
|
||||||
|
SCENETLevel = 2
|
||||||
|
SCERTCEnabled = True
|
||||||
|
SCERTCLevel = 2
|
||||||
|
SCESASEnabled = True
|
||||||
|
SCESASLevel = 2
|
||||||
|
SCEUTILEnabled = True
|
||||||
|
SCEUTILLevel = 2
|
||||||
|
SCEMISCEnabled = True
|
||||||
|
SCEMISCLevel = 2
|
||||||
|
[PostShaderSetting]
|
||||||
|
BloomSettingValue1 = 0.600000
|
||||||
|
BloomSettingValue2 = 0.500000
|
||||||
|
CartoonSettingValue1 = 0.500000
|
||||||
|
ColorCorrectionSettingValue1 = 1.000000
|
||||||
|
ColorCorrectionSettingValue2 = 1.000000
|
||||||
|
ColorCorrectionSettingValue3 = 1.000000
|
||||||
|
ColorCorrectionSettingValue4 = 1.000000
|
||||||
|
ScanlinesSettingValue1 = 1.000000
|
||||||
|
ScanlinesSettingValue2 = 0.500000
|
||||||
|
SharpenSettingValue1 = 1.500000
|
||||||
|
[Achievements]
|
||||||
|
AchievementsEnable = False
|
||||||
|
AchievementsChallengeMode = False
|
||||||
|
AchievementsEncoreMode = False
|
||||||
|
AchievementsUnofficial = False
|
||||||
|
AchievementsLogBadMemReads = False
|
||||||
|
AchievementsSoundEffects = True
|
||||||
|
AchievementsUnlockAudioFile =
|
||||||
|
AchievementsLeaderboardSubmitAudioFile =
|
||||||
|
AchievementsLeaderboardTrackerPos = 3
|
||||||
|
AchievementsLeaderboardStartedOrFailedPos = 3
|
||||||
|
AchievementsLeaderboardSubmittedPos = 3
|
||||||
|
AchievementsProgressPos = 3
|
||||||
|
AchievementsChallengePos = 3
|
||||||
|
AchievementsUnlockedPos = 4
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { PluginManager } from "./plugin-manager";
|
import { PluginManager } from "./plugin-manager";
|
||||||
|
|
||||||
import pcsx2 from './builtin/emulators/com.simeonradivoev.gameflow.pcsx2/package.json';
|
import pcsx2 from './builtin/emulators/com.simeonradivoev.gameflow.pcsx2/package.json';
|
||||||
|
import ppsspp from './builtin/emulators/com.simeonradivoev.gameflow.ppsspp/package.json';
|
||||||
import romm from './builtin/sources/com.simeonradivoev.gameflow.romm/package.json';
|
import romm from './builtin/sources/com.simeonradivoev.gameflow.romm/package.json';
|
||||||
import { PluginDescriptionSchema, PluginDescriptionType, PluginSchema } from "@/bun/types/typesc.schema";
|
import { PluginDescriptionSchema, PluginDescriptionType, PluginSchema } from "@/bun/types/typesc.schema";
|
||||||
|
|
||||||
|
|
@ -9,6 +10,7 @@ export default async function register (pluginManager: PluginManager)
|
||||||
|
|
||||||
const plugins: (PluginDescriptionType & { main: string; load: () => Promise<any>; })[] = [
|
const plugins: (PluginDescriptionType & { main: string; load: () => Promise<any>; })[] = [
|
||||||
{ ...pcsx2, load: () => import('./builtin/emulators/com.simeonradivoev.gameflow.pcsx2/pcsx2') },
|
{ ...pcsx2, load: () => import('./builtin/emulators/com.simeonradivoev.gameflow.pcsx2/pcsx2') },
|
||||||
|
{ ...ppsspp, load: () => import('./builtin/emulators/com.simeonradivoev.gameflow.ppsspp/ppsspp') },
|
||||||
{ ...romm, load: () => import('./builtin/sources/com.simeonradivoev.gameflow.romm/romm') },
|
{ ...romm, load: () => import('./builtin/sources/com.simeonradivoev.gameflow.romm/romm') },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,33 +62,46 @@ export const system = new Elysia({ prefix: '/api/system' })
|
||||||
return new Response(buildNotificationsStream());
|
return new Response(buildNotificationsStream());
|
||||||
})
|
})
|
||||||
.ws('/info/system', {
|
.ws('/info/system', {
|
||||||
response: SystemInfoSchema,
|
response: z.discriminatedUnion('type', [
|
||||||
|
z.object({ type: z.literal('info'), data: SystemInfoSchema }),
|
||||||
|
z.object({ type: z.literal('focus') })
|
||||||
|
]),
|
||||||
async open (ws)
|
async open (ws)
|
||||||
{
|
{
|
||||||
const battery = await si.battery();
|
const battery = await si.battery();
|
||||||
const wifi = await si.wifiConnections();
|
const wifi = await si.wifiConnections();
|
||||||
const bluetooth = await si.bluetoothDevices();
|
const bluetooth = await si.bluetoothDevices();
|
||||||
ws.send({
|
ws.send({
|
||||||
|
type: 'info',
|
||||||
|
data: {
|
||||||
battery: battery,
|
battery: battery,
|
||||||
wifiConnections: wifi,
|
wifiConnections: wifi,
|
||||||
bluetoothDevices: bluetooth
|
bluetoothDevices: bluetooth
|
||||||
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
|
const handleFocus = () => ws.send({ type: 'focus' });
|
||||||
|
events.on('focus', handleFocus);
|
||||||
|
(ws.data as any).dispose = [() => events.removeListener('focus', handleFocus)];
|
||||||
(ws.data as any).observer = setInterval(async () =>
|
(ws.data as any).observer = setInterval(async () =>
|
||||||
{
|
{
|
||||||
const battery = await si.battery();
|
const battery = await si.battery();
|
||||||
const wifi = await si.wifiConnections();
|
const wifi = await si.wifiConnections();
|
||||||
const bluetooth = await si.bluetoothDevices();
|
const bluetooth = await si.bluetoothDevices();
|
||||||
ws.send({
|
ws.send({
|
||||||
|
type: 'info',
|
||||||
|
data: {
|
||||||
battery: battery,
|
battery: battery,
|
||||||
wifiConnections: wifi,
|
wifiConnections: wifi,
|
||||||
bluetoothDevices: bluetooth
|
bluetoothDevices: bluetooth
|
||||||
|
}
|
||||||
}, true);
|
}, true);
|
||||||
}, 1000 * 30);
|
}, 1000 * 30);
|
||||||
},
|
},
|
||||||
close (ws)
|
close (ws)
|
||||||
{
|
{
|
||||||
clearInterval((ws.data as any).observer);
|
clearInterval((ws.data as any).observer);
|
||||||
|
(ws.data as any).dispose.forEach((dispose: any) => dispose());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.get('/drives', async () =>
|
.get('/drives', async () =>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { killBrowser, spawnBrowser } from './utils/browser-spawner';
|
||||||
import { BrowserParams, BuildParams } from './utils/browser-params';
|
import { BrowserParams, BuildParams } from './utils/browser-params';
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
import { EventEmitter } from 'node:stream';
|
import { EventEmitter } from 'node:stream';
|
||||||
|
import { dlopen, FFIType } from "bun:ffi";
|
||||||
|
|
||||||
export default async function init (events: EventEmitter, forceBrowser: boolean, params: BrowserParams)
|
export default async function init (events: EventEmitter, forceBrowser: boolean, params: BrowserParams)
|
||||||
{
|
{
|
||||||
|
|
@ -31,8 +32,6 @@ async function runWebview (events: EventEmitter, params: BrowserParams)
|
||||||
config.WINDOW_HEIGHT = String(params.windowSize?.height);
|
config.WINDOW_HEIGHT = String(params.windowSize?.height);
|
||||||
}
|
}
|
||||||
const webviewWorker = new Worker(webviewPath, {
|
const webviewWorker = new Worker(webviewPath, {
|
||||||
smol: true,
|
|
||||||
ref: false,
|
|
||||||
env: {
|
env: {
|
||||||
...config,
|
...config,
|
||||||
...process.env as any
|
...process.env as any
|
||||||
|
|
@ -41,7 +40,6 @@ async function runWebview (events: EventEmitter, params: BrowserParams)
|
||||||
|
|
||||||
return new Promise((resolve, reject) =>
|
return new Promise((resolve, reject) =>
|
||||||
{
|
{
|
||||||
|
|
||||||
const handleExit = () =>
|
const handleExit = () =>
|
||||||
{
|
{
|
||||||
resolve(true);
|
resolve(true);
|
||||||
|
|
@ -49,6 +47,8 @@ async function runWebview (events: EventEmitter, params: BrowserParams)
|
||||||
webviewWorker.terminate();
|
webviewWorker.terminate();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let pointer: any = undefined;
|
||||||
|
|
||||||
webviewWorker.addEventListener('error', e =>
|
webviewWorker.addEventListener('error', e =>
|
||||||
{
|
{
|
||||||
console.error(e.message);
|
console.error(e.message);
|
||||||
|
|
@ -64,10 +64,35 @@ async function runWebview (events: EventEmitter, params: BrowserParams)
|
||||||
{
|
{
|
||||||
console.log("Webview Destroyed");
|
console.log("Webview Destroyed");
|
||||||
resolve(true);
|
resolve(true);
|
||||||
|
} else if (e.data.type === 'pointer')
|
||||||
|
{
|
||||||
|
pointer = e.data.data;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
events.on('exitapp', handleExit);
|
events.on('exitapp', handleExit);
|
||||||
|
events.on('focus', () =>
|
||||||
|
{
|
||||||
|
if (process.platform === 'win32')
|
||||||
|
{
|
||||||
|
const user32 = dlopen("user32.dll", {
|
||||||
|
SetForegroundWindow: { args: [FFIType.ptr], returns: FFIType.bool },
|
||||||
|
ShowWindow: { args: [FFIType.ptr, FFIType.i32], returns: FFIType.bool },
|
||||||
|
BringWindowToTop: { args: [FFIType.ptr], returns: FFIType.bool },
|
||||||
|
keybd_event: { args: [FFIType.u8, FFIType.u8, FFIType.u32, FFIType.ptr], returns: FFIType.void },
|
||||||
|
});
|
||||||
|
|
||||||
|
const SW_RESTORE = 9;
|
||||||
|
|
||||||
|
if (pointer)
|
||||||
|
{
|
||||||
|
user32.symbols.ShowWindow(pointer, SW_RESTORE);
|
||||||
|
user32.symbols.keybd_event(0, 0, 0, null); // fake input event
|
||||||
|
user32.symbols.BringWindowToTop(pointer);
|
||||||
|
user32.symbols.SetForegroundWindow(pointer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
53
src/bun/controls.ts
Normal file
53
src/bun/controls.ts
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
|
||||||
|
|
||||||
|
import { LaunchGameJob } from './api/jobs/launch-game-job';
|
||||||
|
import { events, taskQueue } from './api/app';
|
||||||
|
|
||||||
|
process.env.SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS = "1";
|
||||||
|
process.env.SDL_JOYSTICK_THREAD = "1";
|
||||||
|
|
||||||
|
export default async function Initialize ()
|
||||||
|
{
|
||||||
|
const { default: sdl } = await import('@kmamal/sdl');
|
||||||
|
const launcherWin = sdl.video.createWindow({ title: "Launcher", visible: false });
|
||||||
|
|
||||||
|
sdl.controller.devices.forEach(d => connectToController(d));
|
||||||
|
sdl.controller.on('deviceAdd', e =>
|
||||||
|
{
|
||||||
|
connectToController(e.device);
|
||||||
|
});
|
||||||
|
|
||||||
|
function connectToController (device: any)
|
||||||
|
{
|
||||||
|
let selectHeld = false;
|
||||||
|
const ctrl = sdl.controller.openDevice(device);
|
||||||
|
console.log("Connected to", device.name);
|
||||||
|
|
||||||
|
ctrl.on("buttonDown", ({ button }) =>
|
||||||
|
{
|
||||||
|
if (button === "back") selectHeld = true;
|
||||||
|
if (button === "start" && selectHeld)
|
||||||
|
{
|
||||||
|
const launchGameTask = taskQueue.findJob(LaunchGameJob.id, LaunchGameJob);
|
||||||
|
if (launchGameTask)
|
||||||
|
{
|
||||||
|
launchGameTask.abort('exit');
|
||||||
|
taskQueue.waitForJob(LaunchGameJob.id).then(() => setTimeout(() => events.emit('focus'), 300));
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
events.emit('focus');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button === 'guide')
|
||||||
|
{
|
||||||
|
events.emit('focus');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ctrl.on("buttonUp", ({ button }) =>
|
||||||
|
{
|
||||||
|
if (button === "back") selectHeld = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -38,12 +38,16 @@ if (process.env.HEADLESS)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Called by user
|
// Using stdout for communication as ipc doesn't seem to work with dev.ts script
|
||||||
app.events.on('exitapp', () =>
|
app.events.on('exitapp', () =>
|
||||||
{
|
{
|
||||||
process.send?.({ type: 'exitapp' });
|
process.stdout.write('exitapp\n');
|
||||||
cleanup();
|
cleanup();
|
||||||
});
|
});
|
||||||
|
app.events.on('focus', () =>
|
||||||
|
{
|
||||||
|
process.stdout.write("focus\n");
|
||||||
|
});
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
await init(app.events, Bun.env.FORCE_BROWSER === "true", {
|
await init(app.events, Bun.env.FORCE_BROWSER === "true", {
|
||||||
|
|
|
||||||
1
src/bun/types/types.d.ts
vendored
1
src/bun/types/types.d.ts
vendored
|
|
@ -28,4 +28,5 @@ declare interface AppEventMap
|
||||||
{
|
{
|
||||||
exitapp: [];
|
exitapp: [];
|
||||||
notification: [FrontendNotification];
|
notification: [FrontendNotification];
|
||||||
|
focus: [];
|
||||||
}
|
}
|
||||||
|
|
@ -27,7 +27,7 @@ export type PluginContextType = z.infer<typeof PluginContextSchema>;
|
||||||
export type PluginDescriptionType = z.infer<typeof PluginDescriptionSchema>;
|
export type PluginDescriptionType = z.infer<typeof PluginDescriptionSchema>;
|
||||||
|
|
||||||
export const ActiveGameSchema = z.object({
|
export const ActiveGameSchema = z.object({
|
||||||
process: z.instanceof(ChildProcess).optional(),
|
process: z.any().optional(),
|
||||||
gameId: z.number(),
|
gameId: z.number(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
command: z.object({ command: z.string(), startDir: z.string().optional() })
|
command: z.object({ command: z.string(), startDir: z.string().optional() })
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { $, type Subprocess } from "bun";
|
import { $, type Subprocess } from "bun";
|
||||||
|
import { ChildProcessWithoutNullStreams } from "node:child_process";
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
|
|
||||||
export type RunBrowserType = "chrome" | "chromium" | "firefox" | "edge";
|
export type RunBrowserType = "chrome" | "chromium" | "firefox" | "edge";
|
||||||
|
|
@ -163,7 +164,7 @@ export async function spawnBrowser ({
|
||||||
return processSub;
|
return processSub;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function killBrowser (browser: Subprocess)
|
export async function killBrowser (browser: Subprocess | ChildProcessWithoutNullStreams)
|
||||||
{
|
{
|
||||||
if (os.platform() === 'linux')
|
if (os.platform() === 'linux')
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,4 +6,5 @@ let size: Size | undefined = undefined;
|
||||||
if (process.env.WINDOW_WIDTH && process.env.WINDOW_HEIGHT)
|
if (process.env.WINDOW_WIDTH && process.env.WINDOW_HEIGHT)
|
||||||
size = { width: Number(process.env.WINDOW_WIDTH), height: Number(process.env.WINDOW_HEIGHT), hint: SizeHint.NONE };
|
size = { width: Number(process.env.WINDOW_WIDTH), height: Number(process.env.WINDOW_HEIGHT), hint: SizeHint.NONE };
|
||||||
const webview = new Webview(process.env.NODE_ENV === 'development', size);
|
const webview = new Webview(process.env.NODE_ENV === 'development', size);
|
||||||
|
self.postMessage({ type: 'pointer', data: webview.unsafeWindowHandle });
|
||||||
webviewWorkerBase(webview);
|
webviewWorkerBase(webview);
|
||||||
33
src/mainview/components/AppCommunication.tsx
Normal file
33
src/mainview/components/AppCommunication.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { SystemInfoContext } from "../scripts/contexts";
|
||||||
|
import { systemApi } from "../scripts/clientApi";
|
||||||
|
import { SystemInfoType } from "@/shared/constants";
|
||||||
|
|
||||||
|
export default function AppCommunication (data: { children: any; })
|
||||||
|
{
|
||||||
|
|
||||||
|
const [systemInfo, setSystemInfo] = useState<SystemInfoType | undefined>();
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
const sub = systemApi.api.system.info.system.subscribe();
|
||||||
|
sub.subscribe(({ data }) =>
|
||||||
|
{
|
||||||
|
switch (data.type)
|
||||||
|
{
|
||||||
|
case "info":
|
||||||
|
setSystemInfo(data.data);
|
||||||
|
break;
|
||||||
|
case "focus":
|
||||||
|
window.focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
document.documentElement.dataset.loaded = "true";
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <SystemInfoContext value={systemInfo}>
|
||||||
|
{data.children}
|
||||||
|
</SystemInfoContext>;
|
||||||
|
}
|
||||||
|
|
@ -464,7 +464,7 @@ const assets = new Set<string>([
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Store basePath resolved from Vite config
|
// Store basePath resolved from Vite config
|
||||||
const BASE_PATH = "/";
|
const BASE_PATH = "./";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { useEffect, useState } from "react";
|
||||||
import { SystemInfoContext } from "../scripts/contexts";
|
import { SystemInfoContext } from "../scripts/contexts";
|
||||||
import { SystemInfoType } from "@/shared/constants";
|
import { SystemInfoType } from "@/shared/constants";
|
||||||
import { systemApi } from "../scripts/clientApi";
|
import { systemApi } from "../scripts/clientApi";
|
||||||
|
import AppCommunication from "../components/AppCommunication";
|
||||||
|
|
||||||
export const Route = createRootRouteWithContext<RouterContext>()({
|
export const Route = createRootRouteWithContext<RouterContext>()({
|
||||||
component: RootComponent,
|
component: RootComponent,
|
||||||
|
|
@ -34,23 +35,11 @@ function RootComponent ()
|
||||||
|
|
||||||
}, [theme]);
|
}, [theme]);
|
||||||
|
|
||||||
const [systemInfo, setSystemInfo] = useState<SystemInfoType | undefined>();
|
|
||||||
useEffect(() =>
|
|
||||||
{
|
|
||||||
const sub = systemApi.api.system.info.system.subscribe();
|
|
||||||
sub.subscribe(({ data }) =>
|
|
||||||
{
|
|
||||||
setSystemInfo(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
document.documentElement.dataset.loaded = "true";
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-device={isMobile ? 'mobile' : ''} data-active-control={control} className="w-screen h-screen overflow-hidden">
|
<div data-device={isMobile ? 'mobile' : ''} data-active-control={control} className="w-screen h-screen overflow-hidden">
|
||||||
<SystemInfoContext value={systemInfo}>
|
<AppCommunication>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
</SystemInfoContext>
|
</AppCommunication>
|
||||||
<Notifications />
|
<Notifications />
|
||||||
<Toaster containerStyle={{ viewTimelineName: 'toasters', viewTransitionName: 'notifications' }} />
|
<Toaster containerStyle={{ viewTimelineName: 'toasters', viewTransitionName: 'notifications' }} />
|
||||||
{/*import.meta.env.DEV && !isMobile &&
|
{/*import.meta.env.DEV && !isMobile &&
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,25 @@
|
||||||
import { expect, test } from 'bun:test';
|
import { expect, test } from 'bun:test';
|
||||||
|
import { resolve } from 'node:path';
|
||||||
|
import './preload';
|
||||||
|
|
||||||
test("uses custom emulator", async () =>
|
test("uses custom emulator", async () =>
|
||||||
{
|
{
|
||||||
const { getValidLaunchCommands: getLaunchCommands } = await import('../bun/api/games/services/launchGameService');
|
const { customEmulators } = await import('@/bun/api/app');
|
||||||
|
customEmulators.set('PCSX2', resolve("./src/tests/mock-roms/mock-emulator.exe"));
|
||||||
|
|
||||||
|
const { getValidLaunchCommands: getLaunchCommands } = await import('@/bun/api/games/services/launchGameService');
|
||||||
const commands = await getLaunchCommands({
|
const commands = await getLaunchCommands({
|
||||||
systemSlug: 'ps2',
|
systemSlug: 'ps2',
|
||||||
gamePath: './src/tests/mock-roms/mock-rom.iso',
|
gamePath: './mock-rom.iso'
|
||||||
customEmulatorConfig: new Map([['PCSX2', "./src/tests/mock-roms/pcsx2.exe"]])
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(commands)
|
expect(commands)
|
||||||
.toSatisfy((d) =>
|
.toSatisfy((d) =>
|
||||||
!!d?.find(c =>
|
{
|
||||||
c?.command.includes("./src/tests/mock-roms/mock-rom.iso") &&
|
const validCommand = d.find(c =>
|
||||||
c.command.includes("./src/tests/mock-roms/pcsx2.exe")
|
c?.command.includes("mock-rom.iso") &&
|
||||||
)
|
c.command.includes("mock-emulator.exe")
|
||||||
);
|
);
|
||||||
|
return !!validCommand;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
0
src/tests/mock-roms/mock-emulator.exe
Normal file
0
src/tests/mock-roms/mock-emulator.exe
Normal file
|
|
@ -1,2 +1,11 @@
|
||||||
import { mock } from 'bun:test';
|
import { beforeAll } from 'bun:test';
|
||||||
|
import { resolve } from 'node:path';
|
||||||
|
|
||||||
|
beforeAll(async () =>
|
||||||
|
{
|
||||||
|
process.env.CUSTOM_STORE_PATH = resolve('./src/tests/mock-store');
|
||||||
|
process.env.CONFIG_CWD = resolve('./src/tests/mock-config');
|
||||||
|
|
||||||
|
const { config } = await import('@/bun/api/app');
|
||||||
|
config.set('downloadPath', resolve('./src/tests/mock-roms'));
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue