Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
2dad0de4f1 | |||
14a65eb5bb | |||
0add9200a6 | |||
ee49da3665 | |||
f478ad9216 | |||
c59dbcc35c | |||
91d38b58c4 | |||
240f60b283 | |||
6a32ad65a5 | |||
6cc7a537b6 | |||
bf4c06ee2d | |||
f26d83f291 | |||
8b2c1a04ee | |||
ce03668252 | |||
58c692a879 | |||
e569d57788 | |||
b75cc8f240 | |||
407b34a884 | |||
890d26e883 | |||
2aff5834b9 | |||
69f2c83109 | |||
dbbd80c6c3 | |||
3479804dca | |||
aaeed669df | |||
7084f40404 | |||
f7e9d7d7db |
71
CHANGELOG.md
71
CHANGELOG.md
@ -1,3 +1,74 @@
|
||||
## 0.18.2
|
||||
|
||||
- Update Rainycolor's domain
|
||||
|
||||
## 0.18.1
|
||||
|
||||
- Keys can now be unbinded with Esc
|
||||
- Fixed CHUNITHM IR behavior on actual keyboards
|
||||
|
||||
## 0.18.0
|
||||
|
||||
- Added new grouping options to the package list
|
||||
|
||||
## 0.17.0
|
||||
|
||||
- Added a package creation prompt
|
||||
- Added a default package icon
|
||||
|
||||
## 0.16.0
|
||||
|
||||
- Fixed the clear cache button not working
|
||||
- Fixed Linux builds
|
||||
- Moved the store tab to the left
|
||||
- "Reapply mods and start" renamed from "Refresh and start" to better convey the meaning
|
||||
- "Reapply mods and start" is no longer necessary when enabling packages from the `local` namespace
|
||||
- Various internationalization additions
|
||||
- STARTLINER now remembers the recently open tab and re-opens it on the next session
|
||||
- Added "Beta" to the title as STARTLINER is approaching feature-completeness
|
||||
- Added full Polish localization :smciota:
|
||||
|
||||
## 0.15.0
|
||||
|
||||
- Added internationalization
|
||||
- Moved some client options to the home page
|
||||
- Added 'dll-game32' and 'dll-game64' entries for native mods that target both Chunithm and Ongeki
|
||||
- Ongeki: added mempatcher support (amdaemon)
|
||||
|
||||
## 0.14.0
|
||||
|
||||
- Added the custom FREE PLAY patch for Verse
|
||||
|
||||
## 0.13.0
|
||||
|
||||
- Added profile imports/exports
|
||||
- Fixed error when trying to open an empty Ongeki profile
|
||||
- Switched the default color scheme from invisible to purple
|
||||
|
||||
## 0.12.1
|
||||
|
||||
- Chunithm: fixed crash when using mempatcher
|
||||
|
||||
## 0.12.0
|
||||
|
||||
- Ongeki: cache and mu3.ini config are now split per-profile (requires mu3-mods 3.7+)
|
||||
- Ongeki: added the few config options of mu3.ini that aren't available in TestMenuConfig, or require a restart
|
||||
- Chunithm: added Lumi+ patches
|
||||
- Added support for a non-standard `games` manifest entry intended for local packages
|
||||
- Expected to be an array containing "ongeki", "chunithm" or both
|
||||
- Example: { "games": ["ongeki"] }
|
||||
- Added a button linking to the profile config folder
|
||||
- Fixed the button linking to the data folder showing up when the folder does not exist
|
||||
- Uninstalled tool packages are no longer automatically deselected, as that caused issues
|
||||
|
||||
## 0.11.1
|
||||
|
||||
- Improved help pages
|
||||
|
||||
## 0.11.0
|
||||
|
||||
- Added help pages
|
||||
|
||||
## 0.10.1
|
||||
|
||||
- Fixed the order of cells in the CHUNITHM keyboard
|
||||
|
25
README.md
25
README.md
@ -1,17 +1,24 @@
|
||||
Looking for
|
||||
|
||||
- maimai DX players willing to help develop/test maimai DX support
|
||||
- translators (any language other than English)
|
||||
|
||||
# STARTLINER
|
||||
|
||||
A simple and easy to use launcher, configuration tool and mod manager
|
||||
for O.N.G.E.K.I. and CHUNITHM, using [Rainycolor Watercolor](https://rainy.patafour.zip).
|
||||
This is a program that seeks to streamline game data configuration, currently supporting O.N.G.E.K.I. and CHUNITHM.
|
||||
|
||||
STARTLINER is four things:
|
||||
|
||||
- a mod installer and updater, powered by [Rainycolor Watercolor](https://rainycolor.org),
|
||||
- a configuration GUI for segatools,
|
||||
- a glorified `start.bat` clicker, with automatic monitor setup and rollback,
|
||||
- [an abstraction allowing data configuration without touching the game directory](https://gitea.tendokyu.moe/akanyan/STARTLINER/wiki/Architecture-details).
|
||||
|
||||
STARTLINER's core design principle is to modify, configure and launch games without tampering with them.
|
||||
This makes it possible to keep data cleaner than ever, and to have several configurations pointing at the same data.
|
||||
|
||||
Made with Rust (Tauri) and Vue. Technically multiplatform. Contributions welcome.
|
||||
|
||||
## Features
|
||||
|
||||
- [Clean](https://gitea.tendokyu.moe/akanyan/STARTLINER/wiki/Architecture-details) data modding
|
||||
- Segatools configuration
|
||||
- Monitor configuration with automatic rollback
|
||||
- Support for multiple configurations pointing at the same data
|
||||
|
||||
## Usage
|
||||
|
||||
Download a prebuilt binary from [Releases](https://gitea.tendokyu.moe/akanyan/STARTLINER/releases) or build it yourself:
|
||||
|
1
TODO.md
1
TODO.md
@ -1,5 +1,6 @@
|
||||
### Short-term
|
||||
|
||||
- i18n
|
||||
- https://gitea.tendokyu.moe/TeamTofuShop/segatools/issues/63
|
||||
|
||||
### Long-term
|
||||
|
231
bun.lock
231
bun.lock
@ -8,15 +8,15 @@
|
||||
"@mdi/font": "7.4.47",
|
||||
"@primevue/forms": "^4.3.3",
|
||||
"@primevue/themes": "^4.3.3",
|
||||
"@tailwindcss/vite": "^4.1.3",
|
||||
"@tauri-apps/api": "^2.4.1",
|
||||
"@tailwindcss/vite": "^4.1.4",
|
||||
"@tauri-apps/api": "^2.5.0",
|
||||
"@tauri-apps/plugin-cli": "^2.2.0",
|
||||
"@tauri-apps/plugin-deep-link": "~2.2.1",
|
||||
"@tauri-apps/plugin-dialog": "~2.2.1",
|
||||
"@tauri-apps/plugin-fs": "^2.2.1",
|
||||
"@tauri-apps/plugin-opener": "^2.2.6",
|
||||
"@tauri-apps/plugin-shell": "~2.2.1",
|
||||
"@tauri-apps/plugin-updater": "^2.7.0",
|
||||
"@tauri-apps/plugin-updater": "^2.7.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"markdown-it": "^14.1.0",
|
||||
@ -24,13 +24,14 @@
|
||||
"primeicons": "^7.0.0",
|
||||
"primevue": "^4.3.3",
|
||||
"roboto-fontface": "^0.10.0",
|
||||
"tailwindcss": "^4.1.3",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"tailwindcss-primeui": "^0.4.0",
|
||||
"vue": "^3.5.13",
|
||||
"vuetify": "^3.8.1",
|
||||
"vue-i18n": "^11.1.3",
|
||||
"vuetify": "^3.8.2",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^2.4.1",
|
||||
"@tauri-apps/cli": "^2.5.0",
|
||||
"@tsconfig/node22": "^22.0.1",
|
||||
"@types/node": "^22.14.1",
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
@ -38,11 +39,11 @@
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"sass": "1.77.8",
|
||||
"sass-embedded": "^1.86.3",
|
||||
"sass-embedded": "^1.87.0",
|
||||
"typescript": "^5.8.3",
|
||||
"unplugin-fonts": "^1.3.1",
|
||||
"unplugin-vue-components": "^0.27.5",
|
||||
"vite": "^6.2.6",
|
||||
"vite": "^6.3.2",
|
||||
"vite-plugin-vuetify": "^2.1.1",
|
||||
"vue-tsc": "^2.2.8",
|
||||
},
|
||||
@ -145,6 +146,12 @@
|
||||
|
||||
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.1", "", {}, "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA=="],
|
||||
|
||||
"@intlify/core-base": ["@intlify/core-base@11.1.3", "", { "dependencies": { "@intlify/message-compiler": "11.1.3", "@intlify/shared": "11.1.3" } }, "sha512-cMuHunYO7LE80azTitcvEbs1KJmtd6g7I5pxlApV3Jo547zdO3h31/0uXpqHc+Y3RKt1wo2y68RGSx77Z1klyA=="],
|
||||
|
||||
"@intlify/message-compiler": ["@intlify/message-compiler@11.1.3", "", { "dependencies": { "@intlify/shared": "11.1.3", "source-map-js": "^1.0.2" } }, "sha512-7rbqqpo2f5+tIcwZTAG/Ooy9C8NDVwfDkvSeDPWUPQW+Dyzfw2o9H103N5lKBxO7wxX9dgCDjQ8Umz73uYw3hw=="],
|
||||
|
||||
"@intlify/shared": ["@intlify/shared@11.1.3", "", {}, "sha512-pTFBgqa/99JRA2H1qfyqv97MKWJrYngXBA/I0elZcYxvJgcCw3mApAoPW3mJ7vx3j+Ti0FyKUFZ4hWxdjKaxvA=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||
@ -183,97 +190,101 @@
|
||||
|
||||
"@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.34.6", "", { "os": "android", "cpu": "arm" }, "sha512-+GcCXtOQoWuC7hhX1P00LqjjIiS/iOouHXhMdiDSnq/1DGTox4SpUvO52Xm+div6+106r+TcvOeo/cxvyEyTgg=="],
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.40.0", "", { "os": "android", "cpu": "arm" }, "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg=="],
|
||||
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.34.6", "", { "os": "android", "cpu": "arm64" }, "sha512-E8+2qCIjciYUnCa1AiVF1BkRgqIGW9KzJeesQqVfyRITGQN+dFuoivO0hnro1DjT74wXLRZ7QF8MIbz+luGaJA=="],
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.40.0", "", { "os": "android", "cpu": "arm64" }, "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w=="],
|
||||
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.34.6", "", { "os": "darwin", "cpu": "arm64" }, "sha512-z9Ib+OzqN3DZEjX7PDQMHEhtF+t6Mi2z/ueChQPLS/qUMKY7Ybn5A2ggFoKRNRh1q1T03YTQfBTQCJZiepESAg=="],
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.40.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ=="],
|
||||
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.34.6", "", { "os": "darwin", "cpu": "x64" }, "sha512-PShKVY4u0FDAR7jskyFIYVyHEPCPnIQY8s5OcXkdU8mz3Y7eXDJPdyM/ZWjkYdR2m0izD9HHWA8sGcXn+Qrsyg=="],
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.40.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA=="],
|
||||
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.34.6", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-YSwyOqlDAdKqs0iKuqvRHLN4SrD2TiswfoLfvYXseKbL47ht1grQpq46MSiQAx6rQEN8o8URtpXARCpqabqxGQ=="],
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.40.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg=="],
|
||||
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.34.6", "", { "os": "freebsd", "cpu": "x64" }, "sha512-HEP4CgPAY1RxXwwL5sPFv6BBM3tVeLnshF03HMhJYCNc6kvSqBgTMmsEjb72RkZBAWIqiPUyF1JpEBv5XT9wKQ=="],
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.40.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.34.6", "", { "os": "linux", "cpu": "arm" }, "sha512-88fSzjC5xeH9S2Vg3rPgXJULkHcLYMkh8faix8DX4h4TIAL65ekwuQMA/g2CXq8W+NJC43V6fUpYZNjaX3+IIg=="],
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.40.0", "", { "os": "linux", "cpu": "arm" }, "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.34.6", "", { "os": "linux", "cpu": "arm" }, "sha512-wM4ztnutBqYFyvNeR7Av+reWI/enK9tDOTKNF+6Kk2Q96k9bwhDDOlnCUNRPvromlVXo04riSliMBs/Z7RteEg=="],
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.40.0", "", { "os": "linux", "cpu": "arm" }, "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.34.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-9RyprECbRa9zEjXLtvvshhw4CMrRa3K+0wcp3KME0zmBe1ILmvcVHnypZ/aIDXpRyfhSYSuN4EPdCCj5Du8FIA=="],
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.40.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.34.6", "", { "os": "linux", "cpu": "arm64" }, "sha512-qTmklhCTyaJSB05S+iSovfo++EwnIEZxHkzv5dep4qoszUMX5Ca4WM4zAVUMbfdviLgCSQOu5oU8YoGk1s6M9Q=="],
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.40.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ=="],
|
||||
|
||||
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.34.6", "", { "os": "linux", "cpu": "none" }, "sha512-4Qmkaps9yqmpjY5pvpkfOerYgKNUGzQpFxV6rnS7c/JfYbDSU0y6WpbbredB5cCpLFGJEqYX40WUmxMkwhWCjw=="],
|
||||
"@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.40.0", "", { "os": "linux", "cpu": "none" }, "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg=="],
|
||||
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.34.6", "", { "os": "linux", "cpu": "ppc64" }, "sha512-Zsrtux3PuaxuBTX/zHdLaFmcofWGzaWW1scwLU3ZbW/X+hSsFbz9wDIp6XvnT7pzYRl9MezWqEqKy7ssmDEnuQ=="],
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.40.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.34.6", "", { "os": "linux", "cpu": "none" }, "sha512-aK+Zp+CRM55iPrlyKiU3/zyhgzWBxLVrw2mwiQSYJRobCURb781+XstzvA8Gkjg/hbdQFuDw44aUOxVQFycrAg=="],
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.40.0", "", { "os": "linux", "cpu": "none" }, "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA=="],
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.34.6", "", { "os": "linux", "cpu": "s390x" }, "sha512-WoKLVrY9ogmaYPXwTH326+ErlCIgMmsoRSx6bO+l68YgJnlOXhygDYSZe/qbUJCSiCiZAQ+tKm88NcWuUXqOzw=="],
|
||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.40.0", "", { "os": "linux", "cpu": "none" }, "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.34.6", "", { "os": "linux", "cpu": "x64" }, "sha512-Sht4aFvmA4ToHd2vFzwMFaQCiYm2lDFho5rPcvPBT5pCdC+GwHG6CMch4GQfmWTQ1SwRKS0dhDYb54khSrjDWw=="],
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.40.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.34.6", "", { "os": "linux", "cpu": "x64" }, "sha512-zmmpOQh8vXc2QITsnCiODCDGXFC8LMi64+/oPpPx5qz3pqv0s6x46ps4xoycfUiVZps5PFn1gksZzo4RGTKT+A=="],
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.40.0", "", { "os": "linux", "cpu": "x64" }, "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ=="],
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.34.6", "", { "os": "win32", "cpu": "arm64" }, "sha512-3/q1qUsO/tLqGBaD4uXsB6coVGB3usxw3qyeVb59aArCgedSF66MPdgRStUd7vbZOsko/CgVaY5fo2vkvPLWiA=="],
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.40.0", "", { "os": "linux", "cpu": "x64" }, "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw=="],
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.34.6", "", { "os": "win32", "cpu": "ia32" }, "sha512-oLHxuyywc6efdKVTxvc0135zPrRdtYVjtVD5GUm55I3ODxhU/PwkQFD97z16Xzxa1Fz0AEe4W/2hzRtd+IfpOA=="],
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.40.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.34.6", "", { "os": "win32", "cpu": "x64" }, "sha512-0PVwmgzZ8+TZ9oGBmdZoQVXflbvuwzN/HRclujpl4N/q3i+y0lqLw8n1bXA8ru3sApDjlmONaNAuYr38y1Kr9w=="],
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.40.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA=="],
|
||||
|
||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.3", "", { "dependencies": { "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.29.2", "tailwindcss": "4.1.3" } }, "sha512-H/6r6IPFJkCfBJZ2dKZiPJ7Ueb2wbL592+9bQEl2r73qbX6yGnmQVIfiUvDRB2YI0a3PWDrzUwkvQx1XW1bNkA=="],
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.40.0", "", { "os": "win32", "cpu": "x64" }, "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ=="],
|
||||
|
||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.3", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.3", "@tailwindcss/oxide-darwin-arm64": "4.1.3", "@tailwindcss/oxide-darwin-x64": "4.1.3", "@tailwindcss/oxide-freebsd-x64": "4.1.3", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.3", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.3", "@tailwindcss/oxide-linux-arm64-musl": "4.1.3", "@tailwindcss/oxide-linux-x64-gnu": "4.1.3", "@tailwindcss/oxide-linux-x64-musl": "4.1.3", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.3", "@tailwindcss/oxide-win32-x64-msvc": "4.1.3" } }, "sha512-t16lpHCU7LBxDe/8dCj9ntyNpXaSTAgxWm1u2XQP5NiIu4KGSyrDJJRlK9hJ4U9yJxx0UKCVI67MJWFNll5mOQ=="],
|
||||
"@tailwindcss/node": ["@tailwindcss/node@4.1.4", "", { "dependencies": { "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.29.2", "tailwindcss": "4.1.4" } }, "sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw=="],
|
||||
|
||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.3", "", { "os": "android", "cpu": "arm64" }, "sha512-cxklKjtNLwFl3mDYw4XpEfBY+G8ssSg9ADL4Wm6//5woi3XGqlxFsnV5Zb6v07dxw1NvEX2uoqsxO/zWQsgR+g=="],
|
||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.4", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.4", "@tailwindcss/oxide-darwin-arm64": "4.1.4", "@tailwindcss/oxide-darwin-x64": "4.1.4", "@tailwindcss/oxide-freebsd-x64": "4.1.4", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.4", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.4", "@tailwindcss/oxide-linux-arm64-musl": "4.1.4", "@tailwindcss/oxide-linux-x64-gnu": "4.1.4", "@tailwindcss/oxide-linux-x64-musl": "4.1.4", "@tailwindcss/oxide-wasm32-wasi": "4.1.4", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.4", "@tailwindcss/oxide-win32-x64-msvc": "4.1.4" } }, "sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ=="],
|
||||
|
||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-mqkf2tLR5VCrjBvuRDwzKNShRu99gCAVMkVsaEOFvv6cCjlEKXRecPu9DEnxp6STk5z+Vlbh1M5zY3nQCXMXhw=="],
|
||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.4", "", { "os": "android", "cpu": "arm64" }, "sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA=="],
|
||||
|
||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-7sGraGaWzXvCLyxrc7d+CCpUN3fYnkkcso3rCzwUmo/LteAl2ZGCDlGvDD8Y/1D3ngxT8KgDj1DSwOnNewKhmg=="],
|
||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg=="],
|
||||
|
||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-E2+PbcbzIReaAYZe997wb9rId246yDkCwAakllAWSGqe6VTg9hHle67hfH6ExjpV2LSK/siRzBUs5wVff3RW9w=="],
|
||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.3", "", { "os": "linux", "cpu": "arm" }, "sha512-GvfbJ8wjSSjbLFFE3UYz4Eh8i4L6GiEYqCtA8j2Zd2oXriPuom/Ah/64pg/szWycQpzRnbDiJozoxFU2oJZyfg=="],
|
||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-35UkuCWQTeG9BHcBQXndDOrpsnt3Pj9NVIB4CgNiKmpG8GnCNXeMczkUpOoqcOhO6Cc/mM2W7kaQ/MTEENDDXg=="],
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.4", "", { "os": "linux", "cpu": "arm" }, "sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-dm18aQiML5QCj9DQo7wMbt1Z2tl3Giht54uVR87a84X8qRtuXxUqnKQkRDK5B4bCOmcZ580lF9YcoMkbDYTXHQ=="],
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.3", "", { "os": "linux", "cpu": "x64" }, "sha512-LMdTmGe/NPtGOaOfV2HuO7w07jI3cflPrVq5CXl+2O93DCewADK0uW1ORNAcfu2YxDUS035eY2W38TxrsqngxA=="],
|
||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.3", "", { "os": "linux", "cpu": "x64" }, "sha512-aalNWwIi54bbFEizwl1/XpmdDrOaCjRFQRgtbv9slWjmNPuJJTIKPHf5/XXDARc9CneW9FkSTqTbyvNecYAEGw=="],
|
||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ=="],
|
||||
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-PEj7XR4OGTGoboTIAdXicKuWl4EQIjKHKuR+bFy9oYN7CFZo0eu74+70O4XuERX4yjqVZGAkCdglBODlgqcCXg=="],
|
||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.4", "", { "os": "linux", "cpu": "x64" }, "sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ=="],
|
||||
|
||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.3", "", { "os": "win32", "cpu": "x64" }, "sha512-T8gfxECWDBENotpw3HR9SmNiHC9AOJdxs+woasRZ8Q/J4VHN0OMs7F+4yVNZ9EVN26Wv6mZbK0jv7eHYuLJLwA=="],
|
||||
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.4", "", { "dependencies": { "@emnapi/core": "^1.4.0", "@emnapi/runtime": "^1.4.0", "@emnapi/wasi-threads": "^1.0.1", "@napi-rs/wasm-runtime": "^0.2.8", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q=="],
|
||||
|
||||
"@tailwindcss/vite": ["@tailwindcss/vite@4.1.3", "", { "dependencies": { "@tailwindcss/node": "4.1.3", "@tailwindcss/oxide": "4.1.3", "tailwindcss": "4.1.3" }, "peerDependencies": { "vite": "^5.2.0 || ^6" } }, "sha512-lUI/QaDxLtlV52Lho6pu07CG9pSnRYLOPmKGIQjyHdTBagemc6HmgZxyjGAQ/5HMPrNeWBfTVIpQl0/jLXvWHQ=="],
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng=="],
|
||||
|
||||
"@tauri-apps/api": ["@tauri-apps/api@2.4.1", "", {}, "sha512-5sYwZCSJb6PBGbBL4kt7CnE5HHbBqwH+ovmOW6ZVju3nX4E3JX6tt2kRklFEH7xMOIwR0btRkZktuLhKvyEQYg=="],
|
||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.4", "", { "os": "win32", "cpu": "x64" }, "sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw=="],
|
||||
|
||||
"@tauri-apps/cli": ["@tauri-apps/cli@2.4.1", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.4.1", "@tauri-apps/cli-darwin-x64": "2.4.1", "@tauri-apps/cli-linux-arm-gnueabihf": "2.4.1", "@tauri-apps/cli-linux-arm64-gnu": "2.4.1", "@tauri-apps/cli-linux-arm64-musl": "2.4.1", "@tauri-apps/cli-linux-riscv64-gnu": "2.4.1", "@tauri-apps/cli-linux-x64-gnu": "2.4.1", "@tauri-apps/cli-linux-x64-musl": "2.4.1", "@tauri-apps/cli-win32-arm64-msvc": "2.4.1", "@tauri-apps/cli-win32-ia32-msvc": "2.4.1", "@tauri-apps/cli-win32-x64-msvc": "2.4.1" }, "bin": { "tauri": "tauri.js" } }, "sha512-9Ta81jx9+57FhtU/mPIckDcOBtPTUdKM75t4+aA0X84b8Sclb0jy1xA8NplmcRzp2fsfIHNngU2NiRxsW5+yOQ=="],
|
||||
"@tailwindcss/vite": ["@tailwindcss/vite@4.1.4", "", { "dependencies": { "@tailwindcss/node": "4.1.4", "@tailwindcss/oxide": "4.1.4", "tailwindcss": "4.1.4" }, "peerDependencies": { "vite": "^5.2.0 || ^6" } }, "sha512-4UQeMrONbvrsXKXXp/uxmdEN5JIJ9RkH7YVzs6AMxC/KC1+Np7WZBaNIco7TEjlkthqxZbt8pU/ipD+hKjm80A=="],
|
||||
|
||||
"@tauri-apps/cli-darwin-arm64": ["@tauri-apps/cli-darwin-arm64@2.4.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-QME7s8XQwy3LWClTVlIlwXVSLKkeJ/z88pr917Mtn9spYOjnBfsgHAgGdmpWD3NfJxjg7CtLbhH49DxoFL+hLg=="],
|
||||
"@tauri-apps/api": ["@tauri-apps/api@2.5.0", "", {}, "sha512-Ldux4ip+HGAcPUmuLT8EIkk6yafl5vK0P0c0byzAKzxJh7vxelVtdPONjfgTm96PbN24yjZNESY8CKo8qniluA=="],
|
||||
|
||||
"@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.4.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-/r89IcW6Ya1sEsFUEH7wLNruDTj7WmDWKGpPy7gATFtQr5JEY4heernqE82isjTUimnHZD8SCr0jA3NceI4ybw=="],
|
||||
"@tauri-apps/cli": ["@tauri-apps/cli@2.5.0", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.5.0", "@tauri-apps/cli-darwin-x64": "2.5.0", "@tauri-apps/cli-linux-arm-gnueabihf": "2.5.0", "@tauri-apps/cli-linux-arm64-gnu": "2.5.0", "@tauri-apps/cli-linux-arm64-musl": "2.5.0", "@tauri-apps/cli-linux-riscv64-gnu": "2.5.0", "@tauri-apps/cli-linux-x64-gnu": "2.5.0", "@tauri-apps/cli-linux-x64-musl": "2.5.0", "@tauri-apps/cli-win32-arm64-msvc": "2.5.0", "@tauri-apps/cli-win32-ia32-msvc": "2.5.0", "@tauri-apps/cli-win32-x64-msvc": "2.5.0" }, "bin": { "tauri": "tauri.js" } }, "sha512-rAtHqG0Gh/IWLjN2zTf3nZqYqbo81oMbqop56rGTjrlWk9pTTAjkqOjSL9XQLIMZ3RbeVjveCqqCA0s8RnLdMg=="],
|
||||
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.4.1", "", { "os": "linux", "cpu": "arm" }, "sha512-9tDijkRB+CchAGjXxYdY9l/XzFpLp1yihUtGXJz9eh+3qIoRI043n3e+6xmU8ZURr7XPnu+R4sCmXs6HD+NCEQ=="],
|
||||
"@tauri-apps/cli-darwin-arm64": ["@tauri-apps/cli-darwin-arm64@2.5.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VuVAeTFq86dfpoBDNYAdtQVLbP0+2EKCHIIhkaxjeoPARR0sLpFHz2zs0PcFU76e+KAaxtEtAJAXGNUc8E1PzQ=="],
|
||||
|
||||
"@tauri-apps/cli-linux-arm64-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.4.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-pnFGDEXBAzS4iDYAVxTRhAzNu3K2XPGflYyBc0czfHDBXopqRgMyj5Q9Wj7HAwv6cM8BqzXINxnb2ZJFGmbSgA=="],
|
||||
"@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.5.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-hUF01sC06cZVa8+I0/VtsHOk9BbO75rd+YdtHJ48xTdcYaQ5QIwL4yZz9OR1AKBTaUYhBam8UX9Pvd5V2/4Dpw=="],
|
||||
|
||||
"@tauri-apps/cli-linux-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.4.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-Hp0zXgeZNKmT+eoJSCxSBUm2QndNuRxR55tmIeNm3vbyUMJN/49uW7nurZ5fBPsacN4Pzwlx1dIMK+Gnr9A69w=="],
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.5.0", "", { "os": "linux", "cpu": "arm" }, "sha512-LQKqttsK252LlqYyX8R02MinUsfFcy3+NZiJwHFgi5Y3+ZUIAED9cSxJkyNtuY5KMnR4RlpgWyLv4P6akN1xhg=="],
|
||||
|
||||
"@tauri-apps/cli-linux-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.4.1", "", { "os": "linux", "cpu": "none" }, "sha512-3T3bo2E4fdYRvzcXheWUeQOVB+LunEEi92iPRgOyuSVexVE4cmHYl+MPJF+EUV28Et0hIVTsHibmDO0/04lAFg=="],
|
||||
"@tauri-apps/cli-linux-arm64-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.5.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-mTQufsPcpdHg5RW0zypazMo4L55EfeE5snTzrPqbLX4yCK2qalN7+rnP8O8GT06xhp6ElSP/Ku1M2MR297SByQ=="],
|
||||
|
||||
"@tauri-apps/cli-linux-x64-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.4.1", "", { "os": "linux", "cpu": "x64" }, "sha512-kLN0FdNONO+2i+OpU9+mm6oTGufRC00e197TtwjpC0N6K2K8130w7Q3FeODIM2CMyg0ov3tH+QWqKW7GNhHFzg=="],
|
||||
"@tauri-apps/cli-linux-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.5.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-rQO1HhRUQqyEaal5dUVOQruTRda/TD36s9kv1hTxZiFuSq3558lsTjAcUEnMAtBcBkps20sbyTJNMT0AwYIk8Q=="],
|
||||
|
||||
"@tauri-apps/cli-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.4.1", "", { "os": "linux", "cpu": "x64" }, "sha512-a8exvA5Ub9eg66a6hsMQKJIkf63QAf9OdiuFKOsEnKZkNN2x0NLgfvEcqdw88VY0UMs9dBoZ1AGbWMeYnLrLwQ=="],
|
||||
"@tauri-apps/cli-linux-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.5.0", "", { "os": "linux", "cpu": "none" }, "sha512-7oS18FN46yDxyw1zX/AxhLAd7T3GrLj3Ai6s8hZKd9qFVzrAn36ESL7d3G05s8wEtsJf26qjXnVF4qleS3dYsA=="],
|
||||
|
||||
"@tauri-apps/cli-win32-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.4.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-4JFrslsMCJQG1c573T9uqQSAbF3j/tMKkMWzsIssv8jvPiP++OG61A2/F+y9te9/Q/O95cKhDK63kaiO5xQaeg=="],
|
||||
"@tauri-apps/cli-linux-x64-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.5.0", "", { "os": "linux", "cpu": "x64" }, "sha512-SG5sFNL7VMmDBdIg3nO3EzNRT306HsiEQ0N90ILe3ZABYAVoPDO/ttpCO37ApLInTzrq/DLN+gOlC/mgZvLw1w=="],
|
||||
|
||||
"@tauri-apps/cli-win32-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.4.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-9eXfFORehYSCRwxg2KodfmX/mhr50CI7wyBYGbPLePCjr5z0jK/9IyW6r0tC+ZVjwpX48dkk7hKiUgI25jHjzA=="],
|
||||
"@tauri-apps/cli-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.5.0", "", { "os": "linux", "cpu": "x64" }, "sha512-QXDM8zp/6v05PNWju5ELsVwF0VH1n6b5pk2E6W/jFbbiwz80Vs1lACl9pv5kEHkrxBj+aWU/03JzGuIj2g3SkQ=="],
|
||||
|
||||
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.4.1", "", { "os": "win32", "cpu": "x64" }, "sha512-60a4Ov7Jrwqz2hzDltlS7301dhSAmM9dxo+IRBD3xz7yobKrgaHXYpWvnRomYItHcDd51VaKc9292H8/eE/gsw=="],
|
||||
"@tauri-apps/cli-win32-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.5.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-pFSHFK6b+o9y4Un8w0gGLwVyFTZaC3P0kQ7umRt/BLDkzD5RnQ4vBM7CF8BCU5nkwmEBUCZd7Wt3TWZxe41o6Q=="],
|
||||
|
||||
"@tauri-apps/cli-win32-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.5.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-EArv1IaRlogdLAQyGlKmEqZqm5RfHCUMhJoedWu7GtdbOMUfSAz6FMX2boE1PtEmNO4An+g188flLeVErrxEKg=="],
|
||||
|
||||
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.5.0", "", { "os": "win32", "cpu": "x64" }, "sha512-lj43EFYbnAta8pd9JnUq87o+xRUR0odz+4rixBtTUwUgdRdwQ2V9CzFtsMu6FQKpFQ6mujRK6P1IEwhL6ADRsQ=="],
|
||||
|
||||
"@tauri-apps/plugin-cli": ["@tauri-apps/plugin-cli@2.2.0", "", { "dependencies": { "@tauri-apps/api": "^2.0.0" } }, "sha512-rvNhMog9rHr01Xk+trBFKJ0eZICIvPkm9GX6ogB89/0hROU/lf+a/sb4vC0wtSeR7zrJuCSxwxYuvHCZheaYFA=="],
|
||||
|
||||
@ -287,7 +298,7 @@
|
||||
|
||||
"@tauri-apps/plugin-shell": ["@tauri-apps/plugin-shell@2.2.1", "", { "dependencies": { "@tauri-apps/api": "^2.0.0" } }, "sha512-G1GFYyWe/KlCsymuLiNImUgC8zGY0tI0Y3p8JgBCWduR5IEXlIJS+JuG1qtveitwYXlfJrsExt3enhv5l2/yhA=="],
|
||||
|
||||
"@tauri-apps/plugin-updater": ["@tauri-apps/plugin-updater@2.7.0", "", { "dependencies": { "@tauri-apps/api": "^2.0.0" } }, "sha512-oBug5UCH2wOsoYk0LW5LEMAT51mszjg11s8eungRH26x/qOrEjLvnuJJoxVVr9nsWowJ6vnpXKS+lUMfFTlvHQ=="],
|
||||
"@tauri-apps/plugin-updater": ["@tauri-apps/plugin-updater@2.7.1", "", { "dependencies": { "@tauri-apps/api": "^2.0.0" } }, "sha512-1OPqEY/z7NDVSeTEMIhD2ss/vXWdpfZ5Th2Mk0KtPR/RA6FKuOTDGZQhxoyYBk0pcZJ+nNZUbl/IujDCLBApjA=="],
|
||||
|
||||
"@trivago/prettier-plugin-sort-imports": ["@trivago/prettier-plugin-sort-imports@5.2.2", "", { "dependencies": { "@babel/generator": "^7.26.5", "@babel/parser": "^7.26.7", "@babel/traverse": "^7.26.7", "@babel/types": "^7.26.7", "javascript-natural-sort": "^0.7.1", "lodash": "^4.17.21" }, "peerDependencies": { "@vue/compiler-sfc": "3.x", "prettier": "2.x - 3.x", "prettier-plugin-svelte": "3.x", "svelte": "4.x || 5.x" }, "optionalPeers": ["@vue/compiler-sfc", "prettier-plugin-svelte", "svelte"] }, "sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA=="],
|
||||
|
||||
@ -461,6 +472,8 @@
|
||||
|
||||
"fastq": ["fastq@1.19.0", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA=="],
|
||||
|
||||
"fdir": ["fdir@6.4.4", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg=="],
|
||||
|
||||
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
@ -653,7 +666,7 @@
|
||||
|
||||
"roboto-fontface": ["roboto-fontface@0.10.0", "", {}, "sha512-OlwfYEgA2RdboZohpldlvJ1xngOins5d7ejqnIBWr9KaMxsnBqotpptRXTyfNRLnFpqzX6sTDt+X+a+6udnU8g=="],
|
||||
|
||||
"rollup": ["rollup@4.34.6", "", { "dependencies": { "@types/estree": "1.0.6" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.34.6", "@rollup/rollup-android-arm64": "4.34.6", "@rollup/rollup-darwin-arm64": "4.34.6", "@rollup/rollup-darwin-x64": "4.34.6", "@rollup/rollup-freebsd-arm64": "4.34.6", "@rollup/rollup-freebsd-x64": "4.34.6", "@rollup/rollup-linux-arm-gnueabihf": "4.34.6", "@rollup/rollup-linux-arm-musleabihf": "4.34.6", "@rollup/rollup-linux-arm64-gnu": "4.34.6", "@rollup/rollup-linux-arm64-musl": "4.34.6", "@rollup/rollup-linux-loongarch64-gnu": "4.34.6", "@rollup/rollup-linux-powerpc64le-gnu": "4.34.6", "@rollup/rollup-linux-riscv64-gnu": "4.34.6", "@rollup/rollup-linux-s390x-gnu": "4.34.6", "@rollup/rollup-linux-x64-gnu": "4.34.6", "@rollup/rollup-linux-x64-musl": "4.34.6", "@rollup/rollup-win32-arm64-msvc": "4.34.6", "@rollup/rollup-win32-ia32-msvc": "4.34.6", "@rollup/rollup-win32-x64-msvc": "4.34.6", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wc2cBWqJgkU3Iz5oztRkQbfVkbxoz5EhnCGOrnJvnLnQ7O0WhQUYyv18qQI79O8L7DdHrrlJNeCHd4VGpnaXKQ=="],
|
||||
"rollup": ["rollup@4.40.0", "", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.40.0", "@rollup/rollup-android-arm64": "4.40.0", "@rollup/rollup-darwin-arm64": "4.40.0", "@rollup/rollup-darwin-x64": "4.40.0", "@rollup/rollup-freebsd-arm64": "4.40.0", "@rollup/rollup-freebsd-x64": "4.40.0", "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", "@rollup/rollup-linux-arm-musleabihf": "4.40.0", "@rollup/rollup-linux-arm64-gnu": "4.40.0", "@rollup/rollup-linux-arm64-musl": "4.40.0", "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", "@rollup/rollup-linux-riscv64-gnu": "4.40.0", "@rollup/rollup-linux-riscv64-musl": "4.40.0", "@rollup/rollup-linux-s390x-gnu": "4.40.0", "@rollup/rollup-linux-x64-gnu": "4.40.0", "@rollup/rollup-linux-x64-musl": "4.40.0", "@rollup/rollup-win32-arm64-msvc": "4.40.0", "@rollup/rollup-win32-ia32-msvc": "4.40.0", "@rollup/rollup-win32-x64-msvc": "4.40.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w=="],
|
||||
|
||||
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||
|
||||
@ -661,47 +674,47 @@
|
||||
|
||||
"sass": ["sass@1.77.8", "", { "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", "source-map-js": ">=0.6.2 <2.0.0" }, "bin": { "sass": "sass.js" } }, "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ=="],
|
||||
|
||||
"sass-embedded": ["sass-embedded@1.86.3", "", { "dependencies": { "@bufbuild/protobuf": "^2.0.0", "buffer-builder": "^0.2.0", "colorjs.io": "^0.5.0", "immutable": "^5.0.2", "rxjs": "^7.4.0", "supports-color": "^8.1.1", "sync-child-process": "^1.0.2", "varint": "^6.0.0" }, "optionalDependencies": { "sass-embedded-android-arm": "1.86.3", "sass-embedded-android-arm64": "1.86.3", "sass-embedded-android-ia32": "1.86.3", "sass-embedded-android-riscv64": "1.86.3", "sass-embedded-android-x64": "1.86.3", "sass-embedded-darwin-arm64": "1.86.3", "sass-embedded-darwin-x64": "1.86.3", "sass-embedded-linux-arm": "1.86.3", "sass-embedded-linux-arm64": "1.86.3", "sass-embedded-linux-ia32": "1.86.3", "sass-embedded-linux-musl-arm": "1.86.3", "sass-embedded-linux-musl-arm64": "1.86.3", "sass-embedded-linux-musl-ia32": "1.86.3", "sass-embedded-linux-musl-riscv64": "1.86.3", "sass-embedded-linux-musl-x64": "1.86.3", "sass-embedded-linux-riscv64": "1.86.3", "sass-embedded-linux-x64": "1.86.3", "sass-embedded-win32-arm64": "1.86.3", "sass-embedded-win32-ia32": "1.86.3", "sass-embedded-win32-x64": "1.86.3" }, "bin": { "sass": "dist/bin/sass.js" } }, "sha512-3pZSp24ibO1hdopj+W9DuiWsZOb2YY6AFRo/jjutKLBkqJGM1nJjXzhAYfzRV+Xn5BX1eTI4bBTE09P0XNHOZg=="],
|
||||
"sass-embedded": ["sass-embedded@1.87.0", "", { "dependencies": { "@bufbuild/protobuf": "^2.0.0", "buffer-builder": "^0.2.0", "colorjs.io": "^0.5.0", "immutable": "^5.0.2", "rxjs": "^7.4.0", "supports-color": "^8.1.1", "sync-child-process": "^1.0.2", "varint": "^6.0.0" }, "optionalDependencies": { "sass-embedded-android-arm": "1.87.0", "sass-embedded-android-arm64": "1.87.0", "sass-embedded-android-ia32": "1.87.0", "sass-embedded-android-riscv64": "1.87.0", "sass-embedded-android-x64": "1.87.0", "sass-embedded-darwin-arm64": "1.87.0", "sass-embedded-darwin-x64": "1.87.0", "sass-embedded-linux-arm": "1.87.0", "sass-embedded-linux-arm64": "1.87.0", "sass-embedded-linux-ia32": "1.87.0", "sass-embedded-linux-musl-arm": "1.87.0", "sass-embedded-linux-musl-arm64": "1.87.0", "sass-embedded-linux-musl-ia32": "1.87.0", "sass-embedded-linux-musl-riscv64": "1.87.0", "sass-embedded-linux-musl-x64": "1.87.0", "sass-embedded-linux-riscv64": "1.87.0", "sass-embedded-linux-x64": "1.87.0", "sass-embedded-win32-arm64": "1.87.0", "sass-embedded-win32-ia32": "1.87.0", "sass-embedded-win32-x64": "1.87.0" }, "bin": { "sass": "dist/bin/sass.js" } }, "sha512-1IA3iTJNh4BkkA/nidKiVwbmkxr9o6LsPegycHMX/JYs255zpocN5GdLF1+onohQCJxbs5ldr8osKV7qNaNBjg=="],
|
||||
|
||||
"sass-embedded-android-arm": ["sass-embedded-android-arm@1.86.3", "", { "os": "android", "cpu": "arm" }, "sha512-UyeXrFzZSvrGbvrWUBcspbsbivGgAgebLGJdSqJulgSyGbA6no3DWQ5Qpdd6+OAUC39BlpPu74Wx9s4RrVuaFw=="],
|
||||
"sass-embedded-android-arm": ["sass-embedded-android-arm@1.87.0", "", { "os": "android", "cpu": "arm" }, "sha512-Z20u/Y1kFDpMbgiloR5YPLxNuMVeKQRC8e/n68oAAxf3u7rDSmNn2msi7USqgT1f2zdBBNawn/ifbFEla6JiHw=="],
|
||||
|
||||
"sass-embedded-android-arm64": ["sass-embedded-android-arm64@1.86.3", "", { "os": "android", "cpu": "arm64" }, "sha512-q+XwFp6WgAv+UgnQhsB8KQ95kppvWAB7DSoJp+8Vino8b9ND+1ai3cUUZPE5u4SnLZrgo5NtrbPvN5KLc4Pfyg=="],
|
||||
"sass-embedded-android-arm64": ["sass-embedded-android-arm64@1.87.0", "", { "os": "android", "cpu": "arm64" }, "sha512-uqeZoBuXm3W2KhxolScAAfWOLHL21e50g7AxlLmG0he7WZsWw6e9kSnmq301iLIFp4kvmXYXbXbNKAeu9ItRYA=="],
|
||||
|
||||
"sass-embedded-android-ia32": ["sass-embedded-android-ia32@1.86.3", "", { "os": "android", "cpu": "ia32" }, "sha512-gTJjVh2cRzvGujXj5ApPk/owUTL5SiO7rDtNLrzYAzi1N5HRuLYXqk3h1IQY3+eCOBjGl7mQ9XyySbJs/3hDvg=="],
|
||||
"sass-embedded-android-ia32": ["sass-embedded-android-ia32@1.87.0", "", { "os": "android", "cpu": "ia32" }, "sha512-hSWTqo2Igdig528cUb1W1+emw9d1J4+nqOoR4tERS04zcwRRFNDiuBT0o5meV7nkEwE982F+h57YdcRXj8gTtg=="],
|
||||
|
||||
"sass-embedded-android-riscv64": ["sass-embedded-android-riscv64@1.86.3", "", { "os": "android", "cpu": "none" }, "sha512-Po3JnyiCS16kd6REo1IMUbFGYtvL9O0rmKaXx5vOuBaJD1LPy2LiSSp7TU7wkJ9IxsTDGzFaSeP1I9qb6D8VVg=="],
|
||||
"sass-embedded-android-riscv64": ["sass-embedded-android-riscv64@1.87.0", "", { "os": "android", "cpu": "none" }, "sha512-kBAPSjiTBLy5ua/0LRNAJwOAARhzFU7gP35fYORJcdBuz1lkIVPVnid1lh9qQ6Ce9MOJcr7VKFtGnTuqVeig5A=="],
|
||||
|
||||
"sass-embedded-android-x64": ["sass-embedded-android-x64@1.86.3", "", { "os": "android", "cpu": "x64" }, "sha512-+7h3jdDv/0kUFx0BvxYlq2fa7CcHiDPlta6k5OxO5K6jyqJwo9hc0Z052BoYEauWTqZ+vK6bB5rv2BIzq4U9nA=="],
|
||||
"sass-embedded-android-x64": ["sass-embedded-android-x64@1.87.0", "", { "os": "android", "cpu": "x64" }, "sha512-ZHMrNdtdMSpJUYco2MesnlPwDTZftD3pqkkOMI2pbqarPoFUKJtP5k80nwCM0sJGtqfNE+O16w9yPght0CMiJg=="],
|
||||
|
||||
"sass-embedded-darwin-arm64": ["sass-embedded-darwin-arm64@1.86.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-EgLwV4ORm5Hr0DmIXo0Xw/vlzwLnfAiqD2jDXIglkBsc5czJmo4/IBdGXOP65TRnsgJEqvbU3aQhuawX5++x9A=="],
|
||||
"sass-embedded-darwin-arm64": ["sass-embedded-darwin-arm64@1.87.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7TK1JWJdCIRSdZv5CJv/HpDz/wIfwUy2FoPz9sVOEj1pDTH0N+VfJd5VutCddIdoQN9jr0ap8vwkc65FbAxV2A=="],
|
||||
|
||||
"sass-embedded-darwin-x64": ["sass-embedded-darwin-x64@1.86.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-dfKhfrGPRNLWLC82vy/vQGmNKmAiKWpdFuWiePRtg/E95pqw+sCu6080Y6oQLfFu37Iq3MpnXiSpDuSo7UnPWA=="],
|
||||
"sass-embedded-darwin-x64": ["sass-embedded-darwin-x64@1.87.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-2JiQzt7FmgUC4MYT2QvbeH/Bi3e76WEhaYoc5P3WyTW8unsHksyTdMuTuYe0Qf9usIyt6bmm5no/4BBw7c8Cig=="],
|
||||
|
||||
"sass-embedded-linux-arm": ["sass-embedded-linux-arm@1.86.3", "", { "os": "linux", "cpu": "arm" }, "sha512-+fVCIH+OR0SMHn2NEhb/VfbpHuUxcPtqMS34OCV3Ka99LYZUJZqth4M3lT/ppGl52mwIVLNYzR4iLe6mdZ6mYA=="],
|
||||
"sass-embedded-linux-arm": ["sass-embedded-linux-arm@1.87.0", "", { "os": "linux", "cpu": "arm" }, "sha512-z5P6INMsGXiUcq1sRRbksyQUhalFFYjTEexuxfSYdK3U2YQMADHubQh8pGzkWvFRPOpnh83RiGuwvpaARYHnsw=="],
|
||||
|
||||
"sass-embedded-linux-arm64": ["sass-embedded-linux-arm64@1.86.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-tYq5rywR53Qtc+0KI6pPipOvW7a47ETY69VxfqI9BR2RKw2hBbaz0bIw6OaOgEBv2/XNwcWb7a4sr7TqgkqKAA=="],
|
||||
"sass-embedded-linux-arm64": ["sass-embedded-linux-arm64@1.87.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-5z+mwJCbGZcg+q+MwdEVSh0ogFK7OSAe175Gsozzr/Izw34Q+RGUw9O82jsV2c4YNuTAQvzEHgIO5cvNvt3Quw=="],
|
||||
|
||||
"sass-embedded-linux-ia32": ["sass-embedded-linux-ia32@1.86.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-CmQ5OkqnaeLdaF+bMqlYGooBuenqm3LvEN9H8BLhjkpWiFW8hnYMetiqMcJjhrXLvDw601KGqA5sr/Rsg5s45g=="],
|
||||
"sass-embedded-linux-ia32": ["sass-embedded-linux-ia32@1.87.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-Xzcp+YPp0iakGL148Jl57CO+MxLuj2jsry3M+rc1cSnDlvkjNVs6TMxaL70GFeV5HdU2V60voYcgE7adDUtJjw=="],
|
||||
|
||||
"sass-embedded-linux-musl-arm": ["sass-embedded-linux-musl-arm@1.86.3", "", { "os": "linux", "cpu": "arm" }, "sha512-SEm65SQknI4pl+mH5Xf231hOkHJyrlgh5nj4qDbiBG6gFeutaNkNIeRgKEg3cflXchCr8iV/q/SyPgjhhzQb7w=="],
|
||||
"sass-embedded-linux-musl-arm": ["sass-embedded-linux-musl-arm@1.87.0", "", { "os": "linux", "cpu": "arm" }, "sha512-4PyqOWhRzyu06RRmpCCBOJdF4BOv7s446wrV6yODtEyyfSIDx3MJabo3KT0oJ1lTWSI/aU3R89bKx0JFXcIHHw=="],
|
||||
|
||||
"sass-embedded-linux-musl-arm64": ["sass-embedded-linux-musl-arm64@1.86.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-4zOr2C/eW89rxb4ozTfn7lBzyyM5ZigA1ZSRTcAR26Qbg/t2UksLdGnVX9/yxga0d6aOi0IvO/7iM2DPPRRotg=="],
|
||||
"sass-embedded-linux-musl-arm64": ["sass-embedded-linux-musl-arm64@1.87.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-HWE5eTRCoKzFZWsxOjDMTF5m4DDTQ0n7NJxSYiUXPBDydr9viPXbGOMYG7WVJLjiF7upr7DYo/mfp/SNTMlZyg=="],
|
||||
|
||||
"sass-embedded-linux-musl-ia32": ["sass-embedded-linux-musl-ia32@1.86.3", "", { "os": "linux", "cpu": "ia32" }, "sha512-84Tcld32LB1loiqUvczWyVBQRCChm0wNLlkT59qF29nxh8njFIVf9yaPgXcSyyjpPoD9Tu0wnq3dvVzoMCh9AQ=="],
|
||||
"sass-embedded-linux-musl-ia32": ["sass-embedded-linux-musl-ia32@1.87.0", "", { "os": "linux", "cpu": "ia32" }, "sha512-aQaPvlRn3kh93PLQvl6BcFKu8Ji92+42blFEkg6nMVvmugD5ZwH2TGFrX25ibx4CYxRpMS4ssF7a0i7vy5HB1Q=="],
|
||||
|
||||
"sass-embedded-linux-musl-riscv64": ["sass-embedded-linux-musl-riscv64@1.86.3", "", { "os": "linux", "cpu": "none" }, "sha512-IxEqoiD7vdNpiOwccybbV93NljBy64wSTkUOknGy21SyV43C8uqESOwTwW9ywa3KufImKm8L3uQAW/B0KhJMWg=="],
|
||||
"sass-embedded-linux-musl-riscv64": ["sass-embedded-linux-musl-riscv64@1.87.0", "", { "os": "linux", "cpu": "none" }, "sha512-o5DxcqiFzET3KRWo+futHr/lhAMBP3tJGGx8YIgpHQYfvDMbsvE0hiFC+nZ/GF9dbcGd+ceIQwfvE5mcc7Gsjw=="],
|
||||
|
||||
"sass-embedded-linux-musl-x64": ["sass-embedded-linux-musl-x64@1.86.3", "", { "os": "linux", "cpu": "x64" }, "sha512-ePeTPXUxPK6JgHcUfnrkIyDtyt+zlAvF22mVZv6y1g/PZFm1lSfX+Za7TYHg9KaYqaaXDiw6zICX4i44HhR8rA=="],
|
||||
"sass-embedded-linux-musl-x64": ["sass-embedded-linux-musl-x64@1.87.0", "", { "os": "linux", "cpu": "x64" }, "sha512-dKxWsu9Wu/CyfzQmHdeiGqrRSzJ85VUjbSx+aP1/7ttmps3SSg+YW95PuqnCOa7GSuSreC3dKKpXHTywUxMLQA=="],
|
||||
|
||||
"sass-embedded-linux-riscv64": ["sass-embedded-linux-riscv64@1.86.3", "", { "os": "linux", "cpu": "none" }, "sha512-NuXQ72dwfNLe35E+RaXJ4Noq4EkFwM65eWwCwxEWyJO9qxOx1EXiCAJii6x8kkOh5daWuMU0VAI1B9RsJaqqQQ=="],
|
||||
"sass-embedded-linux-riscv64": ["sass-embedded-linux-riscv64@1.87.0", "", { "os": "linux", "cpu": "none" }, "sha512-Sy3ESZ4FwBiijvmTA9n+0p0w3MNCue1AgINVPzpAY27EFi0h49eqQm9SWfOkFqmkFS2zFRYowdQOr5Bbr2gOXA=="],
|
||||
|
||||
"sass-embedded-linux-x64": ["sass-embedded-linux-x64@1.86.3", "", { "os": "linux", "cpu": "x64" }, "sha512-t8be9zJ5B82+og9bQmIQ83yMGYZMTMrlGA+uGWtYacmwg6w3093dk91Fx0YzNSZBp3Tk60qVYjCZnEIwy60x0g=="],
|
||||
"sass-embedded-linux-x64": ["sass-embedded-linux-x64@1.87.0", "", { "os": "linux", "cpu": "x64" }, "sha512-+UfjakOcHHKTnEqB3EZ+KqzezQOe1emvy4Rs+eQhLyfekpYuNze/qlRvYxfKTmrtvDiUrIto8MXsyZfMLzkuMA=="],
|
||||
|
||||
"sass-embedded-win32-arm64": ["sass-embedded-win32-arm64@1.86.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-4ghuAzjX4q8Nksm0aifRz8hgXMMxS0SuymrFfkfJlrSx68pIgvAge6AOw0edoZoe0Tf5ZbsWUWamhkNyNxkTvw=="],
|
||||
"sass-embedded-win32-arm64": ["sass-embedded-win32-arm64@1.87.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-m1DS6FYUE0/fv+vt38uQB/kxR4UjnyD+2zcSc298pFmA0aYh/XZIPWw7RxG1HL3KLE1ZrGyu3254MPoxRhs3ig=="],
|
||||
|
||||
"sass-embedded-win32-ia32": ["sass-embedded-win32-ia32@1.86.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-tCaK4zIRq9mLRPxLzBAdYlfCuS/xLNpmjunYxeWkIwlJo+k53h1udyXH/FInnQ2GgEz0xMXyvH3buuPgzwWYsw=="],
|
||||
"sass-embedded-win32-ia32": ["sass-embedded-win32-ia32@1.87.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-JztXLo59GMe2E6g+kCsyiERYhtZgkcyDYx6CrXoSTE5WaE+RbxRiCCCv8/1+hf406f08pUxJ8G0Ody7M5urtBA=="],
|
||||
|
||||
"sass-embedded-win32-x64": ["sass-embedded-win32-x64@1.86.3", "", { "os": "win32", "cpu": "x64" }, "sha512-zS+YNKfTF4SnOfpC77VTb0qNZyTXrxnAezSoRV0xnw6HlY+1WawMSSB6PbWtmbvyfXNgpmJUttoTtsvJjRCucg=="],
|
||||
"sass-embedded-win32-x64": ["sass-embedded-win32-x64@1.87.0", "", { "os": "win32", "cpu": "x64" }, "sha512-4nQErpauvhgSo+7ClumGdjdf9sGx+U9yBgvhI0+zUw+D5YvraVgvA0Lk8Wuwntx2PqnvKUk8YDr/vxHJostv4Q=="],
|
||||
|
||||
"semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
|
||||
@ -725,12 +738,14 @@
|
||||
|
||||
"sync-message-port": ["sync-message-port@1.1.3", "", {}, "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.3", "", {}, "sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g=="],
|
||||
"tailwindcss": ["tailwindcss@4.1.4", "", {}, "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A=="],
|
||||
|
||||
"tailwindcss-primeui": ["tailwindcss-primeui@0.4.0", "", { "peerDependencies": { "tailwindcss": ">=3.1.0" } }, "sha512-YYC7B7Yyzm1/4pEGgpf1ABAhbrKY++LuPoUamnKE7fTPO5Ct/Qr/dT+Uq2yiVhQnaW1zHQpYnThxfksaxhlDfQ=="],
|
||||
|
||||
"tapable": ["tapable@2.2.1", "", {}, "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="],
|
||||
|
||||
"tinyglobby": ["tinyglobby@0.2.13", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw=="],
|
||||
|
||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||
|
||||
"ts-api-utils": ["ts-api-utils@2.0.1", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w=="],
|
||||
@ -765,7 +780,7 @@
|
||||
|
||||
"varint": ["varint@6.0.0", "", {}, "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg=="],
|
||||
|
||||
"vite": ["vite@6.2.6", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw=="],
|
||||
"vite": ["vite@6.3.2", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.3", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.12" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg=="],
|
||||
|
||||
"vite-plugin-vuetify": ["vite-plugin-vuetify@2.1.1", "", { "dependencies": { "@vuetify/loader-shared": "^2.1.0", "debug": "^4.3.3", "upath": "^2.0.1" }, "peerDependencies": { "vite": ">=5", "vue": "^3.0.0", "vuetify": "^3.0.0" } }, "sha512-Pb7bKhQH8qPMzURmEGq2aIqCJkruFNsyf1NcrrtnjsOIkqJPMcBbiP0oJoO8/uAmyB5W/1JTbbUEsyXdMM0QHQ=="],
|
||||
|
||||
@ -775,9 +790,11 @@
|
||||
|
||||
"vue-eslint-parser": ["vue-eslint-parser@10.1.2", "", { "dependencies": { "debug": "^4.4.0", "eslint-scope": "^8.2.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.6.0", "lodash": "^4.17.21", "semver": "^7.6.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0" } }, "sha512-1guOfYgNlD7JH2popr/bt5vc7Mzt6quRCnEbqLgpMHvoHEGV1oImzdqrLd+oMD76cHt8ilBP4cda9WA72TLFDQ=="],
|
||||
|
||||
"vue-i18n": ["vue-i18n@11.1.3", "", { "dependencies": { "@intlify/core-base": "11.1.3", "@intlify/shared": "11.1.3", "@vue/devtools-api": "^6.5.0" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-Pcylh9z9S5+CJAqgbRZ3EKxFIBIrtY5YUppU722GIT65+Nukm0TCqiQegZnNLCZkXGthxe0cpqj0AoM51H+6Gw=="],
|
||||
|
||||
"vue-tsc": ["vue-tsc@2.2.8", "", { "dependencies": { "@volar/typescript": "~2.4.11", "@vue/language-core": "2.2.8" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "./bin/vue-tsc.js" } }, "sha512-jBYKBNFADTN+L+MdesNX/TB3XuDSyaWynKMDgR+yCSln0GQ9Tfb7JS2lr46s2LiFUT1WsmfWsSvIElyxzOPqcQ=="],
|
||||
|
||||
"vuetify": ["vuetify@3.8.1", "", { "peerDependencies": { "typescript": ">=4.7", "vite-plugin-vuetify": ">=2.1.0", "vue": "^3.5.0", "webpack-plugin-vuetify": ">=3.1.0" }, "optionalPeers": ["typescript", "vite-plugin-vuetify", "webpack-plugin-vuetify"] }, "sha512-3qReKBBWIIdJJmwnFU1blVIKHDtnLfIP7kk0MwUrrfjYkWmsDpsymtDnsukkTCnlJ1WvhLr64eQFosr0RVbj9w=="],
|
||||
"vuetify": ["vuetify@3.8.2", "", { "peerDependencies": { "typescript": ">=4.7", "vite-plugin-vuetify": ">=2.1.0", "vue": "^3.5.0", "webpack-plugin-vuetify": ">=3.1.0" }, "optionalPeers": ["typescript", "vite-plugin-vuetify", "webpack-plugin-vuetify"] }, "sha512-UJNFP4egmKJTQ3V3MKOq+7vIUKO7/Fko5G6yUsOW2Rm0VNBvAjgO6VY6EnK3DTqEKN6ugVXDEPw37NQSTGLZvw=="],
|
||||
|
||||
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
|
||||
|
||||
@ -805,8 +822,30 @@
|
||||
|
||||
"@primeuix/forms/@primeuix/utils": ["@primeuix/utils@0.4.1", "", {}, "sha512-5+1NLfyna+gLRPeFTo+xlR0tfPVLuVdidbeahAMLkQga5Rw0LxyUBCyD2/Zv2JkV69o2T+hpEDyddl3VdnYoBw=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.9", "", { "dependencies": { "@emnapi/core": "^1.4.0", "@emnapi/runtime": "^1.4.0", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tauri-apps/plugin-cli/@tauri-apps/api": ["@tauri-apps/api@2.4.1", "", {}, "sha512-5sYwZCSJb6PBGbBL4kt7CnE5HHbBqwH+ovmOW6ZVju3nX4E3JX6tt2kRklFEH7xMOIwR0btRkZktuLhKvyEQYg=="],
|
||||
|
||||
"@tauri-apps/plugin-deep-link/@tauri-apps/api": ["@tauri-apps/api@2.4.1", "", {}, "sha512-5sYwZCSJb6PBGbBL4kt7CnE5HHbBqwH+ovmOW6ZVju3nX4E3JX6tt2kRklFEH7xMOIwR0btRkZktuLhKvyEQYg=="],
|
||||
|
||||
"@tauri-apps/plugin-dialog/@tauri-apps/api": ["@tauri-apps/api@2.4.1", "", {}, "sha512-5sYwZCSJb6PBGbBL4kt7CnE5HHbBqwH+ovmOW6ZVju3nX4E3JX6tt2kRklFEH7xMOIwR0btRkZktuLhKvyEQYg=="],
|
||||
|
||||
"@tauri-apps/plugin-fs/@tauri-apps/api": ["@tauri-apps/api@2.4.1", "", {}, "sha512-5sYwZCSJb6PBGbBL4kt7CnE5HHbBqwH+ovmOW6ZVju3nX4E3JX6tt2kRklFEH7xMOIwR0btRkZktuLhKvyEQYg=="],
|
||||
|
||||
"@tauri-apps/plugin-opener/@tauri-apps/api": ["@tauri-apps/api@2.3.0", "", {}, "sha512-33Z+0lX2wgZbx1SPFfqvzI6su63hCBkbzv+5NexeYjIx7WA9htdOKoRR7Dh3dJyltqS5/J8vQFyybiRoaL0hlA=="],
|
||||
|
||||
"@tauri-apps/plugin-shell/@tauri-apps/api": ["@tauri-apps/api@2.4.1", "", {}, "sha512-5sYwZCSJb6PBGbBL4kt7CnE5HHbBqwH+ovmOW6ZVju3nX4E3JX6tt2kRklFEH7xMOIwR0btRkZktuLhKvyEQYg=="],
|
||||
|
||||
"@vue/compiler-sfc/postcss": ["postcss@8.5.1", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ=="],
|
||||
|
||||
"anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
@ -829,14 +868,34 @@
|
||||
|
||||
"readdirp/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
|
||||
|
||||
"rollup/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="],
|
||||
|
||||
"sass-embedded/immutable": ["immutable@5.0.3", "", {}, "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw=="],
|
||||
|
||||
"unplugin-vue-components/unplugin": ["unplugin@1.16.1", "", { "dependencies": { "acorn": "^8.14.0", "webpack-virtual-modules": "^0.6.2" } }, "sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w=="],
|
||||
|
||||
"vue-i18n/@vue/devtools-api": ["@vue/devtools-api@6.6.4", "", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="],
|
||||
|
||||
"@eslint/config-array/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||
|
||||
"@eslint/eslintrc/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"eslint-plugin-vue/vue-eslint-parser/eslint-scope": ["eslint-scope@7.2.2", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg=="],
|
||||
@ -846,5 +905,17 @@
|
||||
"eslint-plugin-vue/vue-eslint-parser/espree": ["espree@9.6.1", "", { "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" } }, "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ=="],
|
||||
|
||||
"eslint/minimatch/brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/runtime/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@emnapi/core/@emnapi/wasi-threads/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
}
|
||||
}
|
||||
|
17
package.json
17
package.json
@ -14,15 +14,15 @@
|
||||
"@mdi/font": "7.4.47",
|
||||
"@primevue/forms": "^4.3.3",
|
||||
"@primevue/themes": "^4.3.3",
|
||||
"@tailwindcss/vite": "^4.1.3",
|
||||
"@tauri-apps/api": "^2.4.1",
|
||||
"@tailwindcss/vite": "^4.1.4",
|
||||
"@tauri-apps/api": "^2.5.0",
|
||||
"@tauri-apps/plugin-cli": "^2.2.0",
|
||||
"@tauri-apps/plugin-deep-link": "~2.2.1",
|
||||
"@tauri-apps/plugin-dialog": "~2.2.1",
|
||||
"@tauri-apps/plugin-fs": "^2.2.1",
|
||||
"@tauri-apps/plugin-opener": "^2.2.6",
|
||||
"@tauri-apps/plugin-shell": "~2.2.1",
|
||||
"@tauri-apps/plugin-updater": "^2.7.0",
|
||||
"@tauri-apps/plugin-updater": "^2.7.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"markdown-it": "^14.1.0",
|
||||
@ -30,13 +30,14 @@
|
||||
"primeicons": "^7.0.0",
|
||||
"primevue": "^4.3.3",
|
||||
"roboto-fontface": "^0.10.0",
|
||||
"tailwindcss": "^4.1.3",
|
||||
"tailwindcss": "^4.1.4",
|
||||
"tailwindcss-primeui": "^0.4.0",
|
||||
"vue": "^3.5.13",
|
||||
"vuetify": "^3.8.1"
|
||||
"vue-i18n": "^11.1.3",
|
||||
"vuetify": "^3.8.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tauri-apps/cli": "^2.4.1",
|
||||
"@tauri-apps/cli": "^2.5.0",
|
||||
"@tsconfig/node22": "^22.0.1",
|
||||
"@types/node": "^22.14.1",
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
@ -44,11 +45,11 @@
|
||||
"@vue/tsconfig": "^0.5.1",
|
||||
"npm-run-all2": "^7.0.2",
|
||||
"sass": "1.77.8",
|
||||
"sass-embedded": "^1.86.3",
|
||||
"sass-embedded": "^1.87.0",
|
||||
"typescript": "^5.8.3",
|
||||
"unplugin-fonts": "^1.3.1",
|
||||
"unplugin-vue-components": "^0.27.5",
|
||||
"vite": "^6.2.6",
|
||||
"vite": "^6.3.2",
|
||||
"vite-plugin-vuetify": "^2.1.1",
|
||||
"vue-tsc": "^2.2.8"
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
If you're stuck on this screen, restart the game.
|
||||
|
||||
If the problem persists, <a href="https://gitea.tendokyu.moe/Dniel97/SEGAguide/wiki/FAQ#game-is-stuck-at-checking-distribution-server" target="_blank">check your network configuration</a>
|
@ -1,8 +0,0 @@
|
||||
You can access this page any time by right-clicking the START button.
|
||||
|
||||
Additional resources:
|
||||
|
||||
- <a href="https://gitea.tendokyu.moe/Dniel97/SEGAguide/wiki/FAQ" target="_blank">SEGAguide</a>
|
||||
- <a href="https://two-torial.xyz/" target="_blank">two-torial</a>
|
||||
|
||||
## Have fun
|
@ -1,3 +0,0 @@
|
||||
You also have to calibrate the lever, or you may get the error 3301.
|
||||
|
||||
Go to lever settings (<span class="bg-black text-white">レバー設定</span>), move the lever to both edges, then press "end" (<span class="bg-black text-white">終了</span>) and "save" (<span class="bg-black text-white">保存する</span>).
|
@ -1,3 +0,0 @@
|
||||
You might get stuck on this screen for several minutes. _This is normal_. The game just takes a long time to load data.
|
||||
|
||||
If you install <code>7EVENDAYSHOLIDAYS/LoadBoost</code>, subsequent launches will be much faster.
|
@ -1,7 +0,0 @@
|
||||
You might get stuck on the following screen:
|
||||
|
||||
<div class="p-2 mt-1 mb-1 bg-black text-white">Aグループの基準機から設定を取得</div>
|
||||
|
||||
In which case, you should go to the test menu, and in game settings <span class="bg-black text-white">ゲーム設定</span> switch from "follow the standard machine" <span class="bg-black text-white">基準機に従う</span> to "standard machine" <span class="bg-black text-white">基準機</span>.
|
||||
|
||||
The test menu can be accessed with %TESTMENU%.
|
BIN
public/no-icon.png
Normal file
BIN
public/no-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 68 KiB |
370
rust/Cargo.lock
generated
370
rust/Cargo.lock
generated
@ -134,9 +134,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.97"
|
||||
version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
@ -162,7 +162,7 @@ dependencies = [
|
||||
"enumflags2",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"rand 0.9.0",
|
||||
"rand 0.9.1",
|
||||
"raw-window-handle",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
@ -208,9 +208,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-compression"
|
||||
version = "0.4.22"
|
||||
version = "0.4.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59a194f9d963d8099596278594b3107448656ba73831c9d8c783e613ce86da64"
|
||||
checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"futures-core",
|
||||
@ -488,11 +488,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d59b4c170e16f0405a2e95aff44432a0d41aa97675f3d52623effe95792a037"
|
||||
checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2"
|
||||
dependencies = [
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -521,9 +521,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "brotli-decompressor"
|
||||
version = "4.0.2"
|
||||
version = "4.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37"
|
||||
checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd"
|
||||
dependencies = [
|
||||
"alloc-no-stdlib",
|
||||
"alloc-stdlib",
|
||||
@ -644,9 +644,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.18"
|
||||
version = "1.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c"
|
||||
checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@ -732,18 +732,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.35"
|
||||
version = "4.5.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
|
||||
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.35"
|
||||
version = "4.5.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
|
||||
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -1199,9 +1199,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"block2 0.6.0",
|
||||
"block2 0.6.1",
|
||||
"libc",
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dispatch2"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"objc2 0.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1995,9 +2005,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.8"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2"
|
||||
checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
@ -2536,9 +2546,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.6"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f33145a5cbea837164362c7bd596106eb7c5198f97d1ba6f6ebb3223952e488"
|
||||
checksum = "5a064218214dc6a10fbae5ec5fa888d80c45d611aba169222fc272072bf7aef6"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"log",
|
||||
@ -2549,9 +2559,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.6"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43ce13c40ec6956157a3635d97a1ee2df323b263f09ea14165131289cb0f5c19"
|
||||
checksum = "199b7932d97e325aff3a7030e141eafe7f2c6268e1d1b24859b753a627f45254"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2697,9 +2707,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.171"
|
||||
version = "0.2.172"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@ -2770,12 +2780,6 @@ dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lockfree-object-pool"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
@ -2908,10 +2912,10 @@ dependencies = [
|
||||
"dpi",
|
||||
"gtk",
|
||||
"keyboard-types",
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-app-kit",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2-foundation 0.3.1",
|
||||
"once_cell",
|
||||
"png",
|
||||
"serde",
|
||||
@ -3056,9 +3060,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objc2"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59"
|
||||
checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551"
|
||||
dependencies = [
|
||||
"objc2-encode",
|
||||
"objc2-exception-helper",
|
||||
@ -3066,75 +3070,77 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objc2-app-kit"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb"
|
||||
checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"block2 0.6.0",
|
||||
"block2 0.6.1",
|
||||
"libc",
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-cloud-kit",
|
||||
"objc2-core-data",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-core-image",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2-quartz-core 0.3.0",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-quartz-core 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-cloud-kit"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c1948a9be5f469deadbd6bcb86ad7ff9e47b4f632380139722f7d9840c0d42c"
|
||||
checksum = "17614fdcd9b411e6ff1117dfb1d0150f908ba83a7df81b1f118005fe0a8ea15d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"objc2 0.6.0",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-foundation 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-data"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f860f8e841f6d32f754836f51e6bc7777cd7e7053cf18528233f6811d3eceb4"
|
||||
checksum = "291fbbf7d29287518e8686417cf7239c74700fd4b607623140a7d4a3c834329d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"objc2 0.6.0",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-foundation 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-foundation"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925"
|
||||
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"objc2 0.6.0",
|
||||
"dispatch2 0.3.0",
|
||||
"objc2 0.6.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-graphics"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8dca602628b65356b6513290a21a6405b4d4027b8b250f0b98dddbb28b7de02"
|
||||
checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"objc2 0.6.0",
|
||||
"dispatch2 0.3.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-core-foundation",
|
||||
"objc2-io-surface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-core-image"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ffa6bea72bf42c78b0b34e89c0bafac877d5f80bf91e159a5d96ea7f693ca56"
|
||||
checksum = "79b3dc0cc4386b6ccf21c157591b34a7f44c8e75b064f85502901ab2188c007e"
|
||||
dependencies = [
|
||||
"objc2 0.6.0",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-foundation 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3166,25 +3172,25 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objc2-foundation"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998"
|
||||
checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"block2 0.6.0",
|
||||
"block2 0.6.1",
|
||||
"libc",
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-io-surface"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "161a8b87e32610086e1a7a9e9ec39f84459db7b3a0881c1f16ca5a2605581c19"
|
||||
checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
@ -3202,14 +3208,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objc2-osa-kit"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1ac59da3ceebc4a82179b35dc550431ad9458f9cc326e053f49ba371ce76c5a"
|
||||
checksum = "26bb88504b5a050dbba515d2414607bf5e57dd56b107bc5f0351197a3e7bdc5d"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2-foundation 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3227,39 +3233,39 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "objc2-quartz-core"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071"
|
||||
checksum = "90ffb6a0cd5f182dc964334388560b12a57f7b74b3e2dec5e2722aa2dfb2ccd5"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"objc2 0.6.0",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-foundation 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-ui-kit"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "777a571be14a42a3990d4ebedaeb8b54cd17377ec21b92e8200ac03797b3bee1"
|
||||
checksum = "25b1312ad7bc8a0e92adae17aa10f90aae1fb618832f9b993b022b591027daed"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2-foundation 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-web-kit"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b717127e4014b0f9f3e8bba3d3f2acec81f1bde01f656823036e823ed2c94dce"
|
||||
checksum = "91672909de8b1ce1c2252e95bbee8c1649c9ad9d14b9248b3d7b4c47903c47ad"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"block2 0.6.0",
|
||||
"objc2 0.6.0",
|
||||
"block2 0.6.1",
|
||||
"objc2 0.6.1",
|
||||
"objc2-app-kit",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2-foundation 0.3.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3375,8 +3381,8 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b"
|
||||
dependencies = [
|
||||
"objc2 0.6.0",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-osa-kit",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -3810,9 +3816,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.94"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -3854,7 +3860,7 @@ checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"getrandom 0.3.2",
|
||||
"rand 0.9.0",
|
||||
"rand 0.9.1",
|
||||
"ring",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
@ -3922,13 +3928,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
|
||||
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4119,17 +4124,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d"
|
||||
dependencies = [
|
||||
"ashpd",
|
||||
"block2 0.6.0",
|
||||
"dispatch2",
|
||||
"block2 0.6.1",
|
||||
"dispatch2 0.2.0",
|
||||
"glib-sys",
|
||||
"gobject-sys",
|
||||
"gtk-sys",
|
||||
"js-sys",
|
||||
"log",
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-app-kit",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2-foundation 0.3.1",
|
||||
"raw-window-handle",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
@ -4607,9 +4612,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.2"
|
||||
version = "1.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
|
||||
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@ -4918,9 +4923,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tao"
|
||||
version = "0.32.8"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63c8b1020610b9138dd7b1e06cf259ae91aa05c30f3bd0d6b42a03997b92dec1"
|
||||
checksum = "1e59c1f38e657351a2e822eadf40d6a2ad4627b9c25557bc1180ec1b3295ef82"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"core-foundation 0.10.0",
|
||||
@ -4939,9 +4944,9 @@ dependencies = [
|
||||
"ndk",
|
||||
"ndk-context",
|
||||
"ndk-sys",
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2-foundation 0.3.1",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"raw-window-handle",
|
||||
@ -4949,8 +4954,8 @@ dependencies = [
|
||||
"tao-macros",
|
||||
"unicode-segmentation",
|
||||
"url",
|
||||
"windows",
|
||||
"windows-core 0.60.1",
|
||||
"windows 0.61.1",
|
||||
"windows-core 0.61.0",
|
||||
"windows-version",
|
||||
"x11-dl",
|
||||
]
|
||||
@ -4985,9 +4990,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "2.4.1"
|
||||
version = "2.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d08db1ff9e011e04014e737ec022610d756c0eae0b3b3a9037bccaf3003173a"
|
||||
checksum = "e7b0bc1aec81bda6bc455ea98fcaed26b3c98c1648c627ad6ff1c704e8bf8cbc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -5006,9 +5011,10 @@ dependencies = [
|
||||
"log",
|
||||
"mime",
|
||||
"muda",
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-ui-kit",
|
||||
"percent-encoding",
|
||||
"plist",
|
||||
"raw-window-handle",
|
||||
@ -5031,14 +5037,14 @@ dependencies = [
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"window-vibrancy",
|
||||
"windows",
|
||||
"windows 0.61.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fd20e4661c2cce65343319e6e8da256958f5af958cafc47c0d0af66a55dcd17"
|
||||
checksum = "d7a0350f0df1db385ca5c02888a83e0e66655c245b7443db8b78a70da7d7f8fc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
@ -5058,9 +5064,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "458258b19032450ccf975840116ecf013e539eadbb74420bd890e8c56ab2b1a4"
|
||||
checksum = "f93f035551bf7b11b3f51ad9bc231ebbe5e085565527991c16cf326aa38cdf47"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"brotli",
|
||||
@ -5085,9 +5091,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d402813d3b9c773a0fa58697c457c771f10e735498fdcb7b343264d18e5a601f"
|
||||
checksum = "8db4df25e2d9d45de0c4c910da61cd5500190da14ae4830749fee3466dddd112"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@ -5099,9 +5105,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4190775d6ff73fe66d9af44c012739a2659720efd9c0e1e56a918678038699d"
|
||||
checksum = "37a5ebe6a610d1b78a94650896e6f7c9796323f408800cef436e0fa0539de601"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"glob",
|
||||
@ -5120,7 +5126,7 @@ version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5458ae16eac81bdbe8d9da2a9f3e01e8cdedbc381cc1727c01127542c8a61c5"
|
||||
dependencies = [
|
||||
"clap 4.5.35",
|
||||
"clap 4.5.37",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -5199,7 +5205,7 @@ dependencies = [
|
||||
"dunce",
|
||||
"glob",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2-foundation 0.3.1",
|
||||
"open",
|
||||
"schemars",
|
||||
"serde",
|
||||
@ -5208,7 +5214,7 @@ dependencies = [
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.12",
|
||||
"url",
|
||||
"windows",
|
||||
"windows 0.60.0",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
@ -5251,9 +5257,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-updater"
|
||||
version = "2.7.0"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d82da763248e635d60ee4aed56c862290e523acc838d83097171f9a544708387"
|
||||
checksum = "73f05c38afd77a4b8fd98e8fb6f1cdbb5fbb8a46ba181eb2758b05321e3c6209"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"dirs",
|
||||
@ -5283,37 +5289,39 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.5.1"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00ada7ac2f9276f09b8c3afffd3215fd5d9bff23c22df8a7c70e7ef67cacd532"
|
||||
checksum = "00f004905d549854069e6774533d742b03cacfd6f03deb08940a8677586cbe39"
|
||||
dependencies = [
|
||||
"cookie",
|
||||
"dpi",
|
||||
"gtk",
|
||||
"http",
|
||||
"jni",
|
||||
"objc2 0.6.1",
|
||||
"objc2-ui-kit",
|
||||
"raw-window-handle",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.12",
|
||||
"url",
|
||||
"windows",
|
||||
"windows 0.61.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "2.5.1"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf2e5842c57e154af43a20a49c7efee0ce2578c20b4c2bdf266852b422d2e421"
|
||||
checksum = "f85d056f4d4b014fe874814034f3416d57114b617a493a4fe552580851a3f3a2"
|
||||
dependencies = [
|
||||
"gtk",
|
||||
"http",
|
||||
"jni",
|
||||
"log",
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2-foundation 0.3.1",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"raw-window-handle",
|
||||
@ -5324,15 +5332,15 @@ dependencies = [
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webview2-com",
|
||||
"windows",
|
||||
"windows 0.61.1",
|
||||
"wry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "2.3.1"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f037e66c7638cc0a2213f61566932b9a06882b8346486579c90e4b019bac447"
|
||||
checksum = "b2900399c239a471bcff7f15c4399eb1a8c4fe511ba2853e07c996d771a5e0a4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"brotli",
|
||||
@ -5731,19 +5739,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tray-icon"
|
||||
version = "0.20.0"
|
||||
version = "0.20.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d433764348e7084bad2c5ea22c96c71b61b17afe3a11645710f533bd72b6a2b5"
|
||||
checksum = "9f7eee98ec5c90daf179d55c20a49d8c0d043054ce7c26336c09a24d31f14fa0"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"dirs",
|
||||
"libappindicator",
|
||||
"muda",
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-app-kit",
|
||||
"objc2-core-foundation",
|
||||
"objc2-core-graphics",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2-foundation 0.3.1",
|
||||
"once_cell",
|
||||
"png",
|
||||
"serde",
|
||||
@ -6185,15 +6193,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com"
|
||||
version = "0.36.0"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0d606f600e5272b514dbb66539dd068211cc20155be8d3958201b4b5bd79ed3"
|
||||
checksum = "b542b5cfbd9618c46c2784e4d41ba218c336ac70d44c55e47b251033e7d85601"
|
||||
dependencies = [
|
||||
"webview2-com-macros",
|
||||
"webview2-com-sys",
|
||||
"windows",
|
||||
"windows-core 0.60.1",
|
||||
"windows-implement 0.59.0",
|
||||
"windows 0.61.1",
|
||||
"windows-core 0.61.0",
|
||||
"windows-implement 0.60.0",
|
||||
"windows-interface",
|
||||
]
|
||||
|
||||
@ -6210,13 +6218,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com-sys"
|
||||
version = "0.36.0"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfb27fccd3c27f68e9a6af1bcf48c2d82534b8675b83608a4d81446d095a17ac"
|
||||
checksum = "8ae2d11c4a686e4409659d7891791254cf9286d3cfe0eef54df1523533d22295"
|
||||
dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
"windows",
|
||||
"windows-core 0.60.1",
|
||||
"windows 0.61.1",
|
||||
"windows-core 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6256,10 +6264,10 @@ version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c"
|
||||
dependencies = [
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-app-kit",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2-foundation 0.3.1",
|
||||
"raw-window-handle",
|
||||
"windows-sys 0.59.0",
|
||||
"windows-version",
|
||||
@ -6271,11 +6279,24 @@ version = "0.60.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddf874e74c7a99773e62b1c671427abf01a425e77c3d3fb9fb1e4883ea934529"
|
||||
dependencies = [
|
||||
"windows-collections",
|
||||
"windows-collections 0.1.1",
|
||||
"windows-core 0.60.1",
|
||||
"windows-future",
|
||||
"windows-future 0.1.1",
|
||||
"windows-link",
|
||||
"windows-numerics",
|
||||
"windows-numerics 0.1.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.61.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
|
||||
dependencies = [
|
||||
"windows-collections 0.2.0",
|
||||
"windows-core 0.61.0",
|
||||
"windows-future 0.2.0",
|
||||
"windows-link",
|
||||
"windows-numerics 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6287,6 +6308,15 @@ dependencies = [
|
||||
"windows-core 0.60.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-collections"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
||||
dependencies = [
|
||||
"windows-core 0.61.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.60.1"
|
||||
@ -6323,6 +6353,16 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-future"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
|
||||
dependencies = [
|
||||
"windows-core 0.61.0",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-implement"
|
||||
version = "0.59.0"
|
||||
@ -6372,6 +6412,16 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-numerics"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
||||
dependencies = [
|
||||
"windows-core 0.61.0",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.4.0"
|
||||
@ -6771,12 +6821,12 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
|
||||
[[package]]
|
||||
name = "wry"
|
||||
version = "0.50.5"
|
||||
version = "0.51.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b19b78efae8b853c6c817e8752fc1dbf9cab8a8ffe9c30f399bd750ccf0f0730"
|
||||
checksum = "c886a0a9d2a94fd90cfa1d929629b79cfefb1546e2c7430c63a47f0664c0e4e2"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"block2 0.6.0",
|
||||
"block2 0.6.1",
|
||||
"cookie",
|
||||
"crossbeam-channel",
|
||||
"dpi",
|
||||
@ -6790,10 +6840,10 @@ dependencies = [
|
||||
"kuchikiki",
|
||||
"libc",
|
||||
"ndk",
|
||||
"objc2 0.6.0",
|
||||
"objc2 0.6.1",
|
||||
"objc2-app-kit",
|
||||
"objc2-core-foundation",
|
||||
"objc2-foundation 0.3.0",
|
||||
"objc2-foundation 0.3.1",
|
||||
"objc2-ui-kit",
|
||||
"objc2-web-kit",
|
||||
"once_cell",
|
||||
@ -6807,8 +6857,8 @@ dependencies = [
|
||||
"webkit2gtk",
|
||||
"webkit2gtk-sys",
|
||||
"webview2-com",
|
||||
"windows",
|
||||
"windows-core 0.60.1",
|
||||
"windows 0.61.1",
|
||||
"windows-core 0.61.0",
|
||||
"windows-version",
|
||||
"x11-dl",
|
||||
]
|
||||
@ -7075,15 +7125,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zopfli"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946"
|
||||
checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"crc32fast",
|
||||
"lockfree-object-pool",
|
||||
"log",
|
||||
"once_cell",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
use std::hash::{DefaultHasher, Hash, Hasher};
|
||||
use std::path::Path;
|
||||
use std::time::SystemTime;
|
||||
use crate::model::config::GlobalConfig;
|
||||
use crate::model::patch::PatchFileVec;
|
||||
use crate::model::patch::{PatchFileVec, PatchList};
|
||||
use crate::pkg::{Feature, Status};
|
||||
use crate::profiles::types::Profile;
|
||||
use crate::{model::misc::Game, pkg::PkgKey};
|
||||
@ -165,4 +166,22 @@ impl AppData {
|
||||
panic!("unable to initialize the logger? {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn patches_enabled(&self, game_target: impl AsRef<Path>, amd_target: impl AsRef<Path>) -> Result<Vec<&PatchList>> {
|
||||
let ch1 = sha256::try_digest(game_target.as_ref())?;
|
||||
let ch2 = sha256::try_digest(amd_target.as_ref())?;
|
||||
|
||||
let mut res = Vec::new();
|
||||
for pfile in &self.patch_vec.0 {
|
||||
for plist in &pfile.0 {
|
||||
let this_hash = plist.sha256.to_ascii_lowercase();
|
||||
log::debug!("checking {}", this_hash);
|
||||
if this_hash == ch1 || this_hash == ch2 {
|
||||
log::debug!("enabling {this_hash}");
|
||||
res.push(plist);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
140
rust/src/cmd.rs
140
rust/src/cmd.rs
@ -1,11 +1,12 @@
|
||||
use ini::Ini;
|
||||
use log;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use std::path::PathBuf;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::fs;
|
||||
use tauri::{AppHandle, Manager, State};
|
||||
use crate::model::config::GlobalConfigField;
|
||||
use crate::model::local::PackageManifest;
|
||||
use crate::model::misc::Game;
|
||||
use crate::model::patch::Patch;
|
||||
use crate::modules::package::prepare_dlls;
|
||||
@ -65,13 +66,20 @@ pub async fn startline(app: AppHandle, refresh: bool) -> Result<(), String> {
|
||||
let mut amd_dlls = Vec::new();
|
||||
if let Some(p) = &appd.profile {
|
||||
hash = appd.sum_packages(p);
|
||||
(game_dlls, amd_dlls) = prepare_dlls(p.mod_pkgs(), &appd.pkgs).map_err(|e| e.to_string())?
|
||||
(game_dlls, amd_dlls) = prepare_dlls(p.meta.game, p.mod_pkgs(), &appd.pkgs).map_err(|e| e.to_string())?
|
||||
}
|
||||
if let Some(p) = &appd.profile {
|
||||
log::debug!("{}", hash);
|
||||
|
||||
let patches_enabled = appd.patches_enabled(
|
||||
&p.data.sgt.target,
|
||||
&p.data.sgt.target.parent().unwrap().join("amdaemon.exe")
|
||||
).map_err(|e| e.to_string())?;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let info = p.prepare_display()
|
||||
.map_err(|e| e.to_string())?;
|
||||
let lineup_res = p.line_up(hash, refresh, &appd.patch_vec).await
|
||||
let lineup_res = p.line_up(hash, refresh, patches_enabled).await
|
||||
.map_err(|e| e.to_string());
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
@ -159,6 +167,65 @@ pub async fn toggle_package(state: State<'_, tokio::sync::Mutex<AppData>>, key:
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn create_package(
|
||||
name: String,
|
||||
description: String,
|
||||
website: String,
|
||||
r#type: String,
|
||||
games: Vec<Game>
|
||||
) -> Result<(), String> {
|
||||
log::debug!("invoke: create_package");
|
||||
|
||||
let dir = util::pkg_dir_of("local", &name);
|
||||
|
||||
if dir.exists() {
|
||||
return Err("Package already exists".to_owned());
|
||||
}
|
||||
|
||||
let mut installers = Vec::new();
|
||||
|
||||
if r#type == "segatools" {
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert(
|
||||
"identifier".to_owned(),
|
||||
serde_json::Value::String("segatools".to_owned())
|
||||
);
|
||||
installers.push(map);
|
||||
} else if r#type == "native" {
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert(
|
||||
"identifier".to_owned(),
|
||||
serde_json::Value::String("native_mod".to_owned())
|
||||
);
|
||||
map.insert(
|
||||
"dll-game".to_owned(),
|
||||
serde_json::Value::String("some.dll".to_owned())
|
||||
);
|
||||
map.insert(
|
||||
"dll-amdaemon".to_owned(),
|
||||
serde_json::Value::String("another.dll".to_owned())
|
||||
);
|
||||
installers.push(map);
|
||||
}
|
||||
|
||||
let manifest = PackageManifest {
|
||||
name,
|
||||
version_number: "1.0.0".to_owned(),
|
||||
description,
|
||||
website_url: website,
|
||||
dependencies: BTreeSet::new(),
|
||||
installers,
|
||||
games: Some(games)
|
||||
};
|
||||
|
||||
std::fs::create_dir(&dir).map_err(|e| e.to_string())?;
|
||||
let json = serde_json::to_string_pretty(&manifest).map_err(|e| e.to_string())?;
|
||||
std::fs::write(dir.join("manifest.json"), json).map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn reload_all_packages(state: State<'_, tokio::sync::Mutex<AppData>>) -> Result<(), String> {
|
||||
log::debug!("invoke: reload_all_packages");
|
||||
@ -184,12 +251,16 @@ pub async fn get_all_packages(state: State<'_, Mutex<AppData>>) -> Result<HashMa
|
||||
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_game_packages(state: State<'_, Mutex<AppData>>, game: Game) -> Result<Vec<PkgKey>, ()> {
|
||||
log::debug!("invoke: get_game_packages {game}");
|
||||
pub async fn get_game_packages(state: State<'_, Mutex<AppData>>, game: Option<Game>) -> Result<Vec<PkgKey>, ()> {
|
||||
log::debug!("invoke: get_game_packages {game:?}");
|
||||
|
||||
let appd = state.lock().await;
|
||||
|
||||
Ok(appd.pkgs.get_game_list(game))
|
||||
if let Some(game) = game {
|
||||
Ok(appd.pkgs.get_game_list(game))
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@ -398,10 +469,61 @@ pub async fn load_segatools_ini(state: State<'_, Mutex<AppData>>, path: PathBuf)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn create_shortcut(app: AppHandle, profile_meta: ProfileMeta) -> Result<(), String> {
|
||||
pub async fn create_shortcut(_app: AppHandle, profile_meta: ProfileMeta) -> Result<(), String> {
|
||||
log::debug!("invoke: create_shortcut({:?})", profile_meta);
|
||||
|
||||
util::create_shortcut(app, &profile_meta).map_err(|e| e.to_string())
|
||||
#[cfg(target_os = "windows")]
|
||||
return util::create_shortcut(_app, &profile_meta).map_err(|e| e.to_string());
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
return Err("unsupported".to_owned());
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn export_profile(state: State<'_, Mutex<AppData>>, export_keychip: bool, files: Vec<String>) -> Result<(), String> {
|
||||
log::debug!("invoke: export_profile({:?}, {:?} files)", export_keychip, files.len());
|
||||
|
||||
let appd = state.lock().await;
|
||||
match &appd.profile {
|
||||
Some(p) => {
|
||||
p.export(export_keychip, files)
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
None => {
|
||||
let err = "export_profile: no profile".to_owned();
|
||||
log::error!("{}", err);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn import_profile(path: PathBuf) -> Result<(), String> {
|
||||
log::debug!("invoke: import_profile({:?})", path);
|
||||
|
||||
Profile::import(path).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn clear_cache(state: State<'_, Mutex<AppData>>) -> Result<(), String> {
|
||||
log::debug!("invoke: clear_cache");
|
||||
|
||||
let appd = state.lock().await;
|
||||
if let Some(p) = &appd.profile {
|
||||
let dir = p.data_dir().join("mu3-mods-cache");
|
||||
let path = dir.join("data_cache.bin");
|
||||
if path.exists() {
|
||||
std::fs::remove_file(path).map_err(|e| e.to_string())?;
|
||||
}
|
||||
let path = dir.join("data_fumen_analysis_cache.bin");
|
||||
if path.exists() {
|
||||
std::fs::remove_file(path).map_err(|e| e.to_string())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@ -409,7 +531,7 @@ pub async fn list_platform_capabilities() -> Result<Vec<String>, ()> {
|
||||
log::debug!("invoke: list_platform_capabilities");
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
return Ok(vec!["display".to_owned()]);
|
||||
return Ok(vec!["display".to_owned(), "shortcut".to_owned(), "chunithm".to_owned()]);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
return Ok(vec!["wine".to_owned()]);
|
||||
|
@ -193,6 +193,7 @@ pub async fn run(_args: Vec<String>) {
|
||||
cmd::install_package,
|
||||
cmd::delete_package,
|
||||
cmd::toggle_package,
|
||||
cmd::create_package,
|
||||
|
||||
cmd::list_profiles,
|
||||
cmd::init_profile,
|
||||
@ -205,6 +206,9 @@ pub async fn run(_args: Vec<String>) {
|
||||
cmd::save_current_profile,
|
||||
cmd::load_segatools_ini,
|
||||
cmd::create_shortcut,
|
||||
cmd::export_profile,
|
||||
cmd::import_profile,
|
||||
cmd::clear_cache,
|
||||
|
||||
cmd::get_global_config,
|
||||
cmd::set_global_config,
|
||||
@ -333,7 +337,7 @@ async fn update(app: tauri::AppHandle) -> tauri_plugin_updater::Result<()> {
|
||||
fn open_window(apph: AppHandle) -> anyhow::Result<()> {
|
||||
let config = apph.config().clone();
|
||||
tauri::WebviewWindowBuilder::new(&apph, "main", tauri::WebviewUrl::App("index.html".into()))
|
||||
.title(format!("STARTLINER {}", config.version.unwrap_or_default()))
|
||||
.title(format!("STARTLINER {} Beta", config.version.unwrap_or_default()))
|
||||
.inner_size(900f64, 600f64)
|
||||
.min_inner_size(900f64, 600f64)
|
||||
.build()?;
|
||||
|
@ -1,20 +1,24 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::pkg::{Status, PkgKey, PkgKeyVersion};
|
||||
use crate::pkg::{PkgKey, PkgKeyVersion};
|
||||
|
||||
use super::misc::Game;
|
||||
|
||||
// manifest.json
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct PackageManifest {
|
||||
pub name: String,
|
||||
pub version_number: String,
|
||||
pub website_url: String,
|
||||
pub description: String,
|
||||
pub dependencies: BTreeSet<PkgKeyVersion>,
|
||||
|
||||
#[serde(default)]
|
||||
pub installers: Vec<BTreeMap<String, serde_json::Value>>
|
||||
pub installers: Vec<BTreeMap<String, serde_json::Value>>,
|
||||
|
||||
#[serde(default)]
|
||||
pub games: Option<Vec<Game>>,
|
||||
}
|
||||
|
||||
pub type PackageList = BTreeMap<PkgKey, PackageListEntry>;
|
||||
@ -22,6 +26,5 @@ pub type PackageList = BTreeMap<PkgKey, PackageListEntry>;
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct PackageListEntry {
|
||||
pub version: String,
|
||||
pub status: Status,
|
||||
pub games: Vec<Game>,
|
||||
}
|
@ -71,10 +71,17 @@ impl Game {
|
||||
|
||||
pub fn has_module(&self, module: ProfileModule) -> bool {
|
||||
match self {
|
||||
Game::Ongeki => make_bitflags!(ProfileModule::{Segatools | Display | Network | BepInEx | Mu3Ini | Keyboard}),
|
||||
Game::Ongeki => make_bitflags!(ProfileModule::{Segatools | Display | Network | BepInEx | Mu3Ini | Keyboard | Mempatcher}),
|
||||
Game::Chunithm => make_bitflags!(ProfileModule::{Segatools | Display | Network | Keyboard | Mempatcher}),
|
||||
}.contains(module)
|
||||
}
|
||||
|
||||
pub fn bitness(&self) -> i32 {
|
||||
match self {
|
||||
Game::Ongeki => 64,
|
||||
Game::Chunithm => 32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Game {
|
||||
|
@ -42,6 +42,7 @@ pub struct Patch {
|
||||
pub enum PatchData {
|
||||
Normal(NormalPatch),
|
||||
Number(NumberPatch),
|
||||
Hex(HexPatch),
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
@ -65,6 +66,12 @@ pub struct NumberPatch {
|
||||
pub max: i32
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
pub struct HexPatch {
|
||||
pub offset: u64,
|
||||
pub off: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Serialize for Patch {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: Serializer {
|
||||
@ -83,6 +90,11 @@ impl Serialize for Patch {
|
||||
state.serialize_field("size", &patch.size)?;
|
||||
state.serialize_field("min", &patch.min)?;
|
||||
state.serialize_field("max", &patch.max)?;
|
||||
},
|
||||
PatchData::Hex(patch) => {
|
||||
state.serialize_field("type", "hex")?;
|
||||
state.serialize_field("offset", &patch.offset)?;
|
||||
state.serialize_field("off", &patch.off)?;
|
||||
}
|
||||
}
|
||||
state.end()
|
||||
@ -114,6 +126,23 @@ impl<'de> serde::Deserialize<'de> for Patch {
|
||||
.ok_or_else(|| de::Error::missing_field("max"))?
|
||||
).map_err(|_| de::Error::missing_field("max"))?
|
||||
}),
|
||||
Some("hex") => {
|
||||
let mut off_list = Vec::new();
|
||||
for off in value.get("off").and_then(Value::as_array).unwrap() {
|
||||
off_list.push(u8::try_from(
|
||||
off.as_u64().ok_or_else(|| de::Error::missing_field("off"))?
|
||||
).map_err(|_| de::Error::missing_field("off"))?);
|
||||
}
|
||||
// for off in value.get("off").and_then(Value::as_str).unwrap().bytes() {
|
||||
// off_list.push(off);
|
||||
// }
|
||||
PatchData::Hex(HexPatch {
|
||||
offset: value.get("offset")
|
||||
.and_then(Value::as_u64)
|
||||
.ok_or_else(|| de::Error::missing_field("offset"))?,
|
||||
off: off_list
|
||||
})
|
||||
},
|
||||
None => {
|
||||
let mut patches = vec![];
|
||||
for patch in value.get("patches").and_then(Value::as_array).unwrap() {
|
||||
|
@ -108,7 +108,10 @@ impl Display {
|
||||
Game::Ongeki => 60,
|
||||
},
|
||||
borderless_fullscreen: true,
|
||||
#[cfg(target_os = "windows")]
|
||||
dont_switch_primary: false,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
dont_switch_primary: true,
|
||||
monitor_index_override: None,
|
||||
}
|
||||
}
|
||||
@ -141,7 +144,7 @@ pub struct BepInEx {
|
||||
pub console: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(default)]
|
||||
pub struct Wine {
|
||||
pub runtime: PathBuf,
|
||||
@ -166,11 +169,26 @@ pub enum Mu3Audio {
|
||||
Excl2Ch,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(default)]
|
||||
pub struct Mu3Ini {
|
||||
pub audio: Option<Mu3Audio>,
|
||||
pub sample_rate: i32,
|
||||
pub blacklist: Option<(i32, i32)>,
|
||||
pub gp: i32,
|
||||
pub enable_bonus_tracks: bool,
|
||||
}
|
||||
|
||||
impl Default for Mu3Ini {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
audio: Some(Mu3Audio::Shared),
|
||||
sample_rate: 48_000,
|
||||
blacklist: Some((10000, 19999)),
|
||||
gp: 999,
|
||||
enable_bonus_tracks: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
|
8
rust/src/modules/display_linux.rs
Normal file
8
rust/src/modules/display_linux.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use ini::Ini;
|
||||
use crate::model::{misc::Game, profile::Display};
|
||||
|
||||
impl Display {
|
||||
pub fn line_up(&self, _game: Game, _ini: &mut Ini) {
|
||||
// nop
|
||||
}
|
||||
}
|
@ -117,12 +117,16 @@ impl Keyboard {
|
||||
}
|
||||
}
|
||||
Keyboard::Chunithm(kb) => {
|
||||
let mut enabled_ir = false;
|
||||
if kb.enabled {
|
||||
for (i, cell) in kb.cell.iter().enumerate() {
|
||||
ini.with_section(Some("slider")).set(format!("cell{}", i + 1), cell.to_string());
|
||||
}
|
||||
for (i, ir) in kb.ir.iter().enumerate() {
|
||||
ini.with_section(Some("ir")).set(format!("ir{}", i + 1), ir.to_string());
|
||||
ini.with_section(Some("ir")).set(format!("ir{}", i + 1), (*ir).to_string());
|
||||
if i > 0 && *ir != 0 {
|
||||
enabled_ir = true;
|
||||
}
|
||||
}
|
||||
ini.with_section(Some("io3"))
|
||||
.set("test", kb.test.to_string())
|
||||
@ -140,8 +144,13 @@ impl Keyboard {
|
||||
.set("service", "0")
|
||||
.set("coin", "0");
|
||||
}
|
||||
ini.with_section(Some("io3"))
|
||||
.set("ir", "0");
|
||||
if enabled_ir {
|
||||
ini.with_section(Some("io3"))
|
||||
.set("ir", "0");
|
||||
} else {
|
||||
ini.with_section(Some("io3"))
|
||||
.set("ir", kb.ir[0].to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,25 +1,23 @@
|
||||
use std::path::Path;
|
||||
use anyhow::Result;
|
||||
use crate::model::patch::{Patch, PatchData, PatchFileVec, PatchSelection, PatchSelectionData};
|
||||
use crate::model::patch::{Patch, PatchData, PatchList, PatchSelection, PatchSelectionData};
|
||||
|
||||
impl PatchSelection {
|
||||
pub async fn render_to_file(
|
||||
&self,
|
||||
filename: &str,
|
||||
patches: &PatchFileVec,
|
||||
patch_lists: &Vec<&PatchList>,
|
||||
path: impl AsRef<Path>
|
||||
) -> Result<()> {
|
||||
let mut res = "".to_owned();
|
||||
|
||||
for file in &patches.0 {
|
||||
for list in &file.0 {
|
||||
if list.filename != filename {
|
||||
continue;
|
||||
}
|
||||
for patch in &list.patches {
|
||||
if let Some(selection) = self.0.get(&patch.id) {
|
||||
res += &Self::render(filename, patch, selection);
|
||||
}
|
||||
for list in patch_lists {
|
||||
if list.filename != filename {
|
||||
continue;
|
||||
}
|
||||
for patch in &list.patches {
|
||||
if let Some(selection) = self.0.get(&patch.id) {
|
||||
res += &Self::render(filename, patch, selection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -52,6 +50,21 @@ impl PatchSelection {
|
||||
} else {
|
||||
log::error!("invalid number patch {:?}", patch);
|
||||
}
|
||||
},
|
||||
PatchData::Hex(data) => {
|
||||
if let PatchSelectionData::Hex(val) = sel {
|
||||
res += &format!("{} F+{:X} ", filename, data.offset);
|
||||
for byte in val {
|
||||
res += &format!("{:02X}", byte);
|
||||
}
|
||||
res += " ";
|
||||
for byte in &data.off {
|
||||
res += &format!("{:02X}", byte);
|
||||
}
|
||||
} else {
|
||||
log::error!("invalid number patch {:?}", patch);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
format!("{}\n", res)
|
||||
|
@ -7,4 +7,7 @@ pub mod keyboard;
|
||||
pub mod mempatcher;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod display_windows;
|
||||
pub mod display_windows;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod display_linux;
|
@ -1,11 +1,11 @@
|
||||
use std::path::Path;
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use ini::Ini;
|
||||
use crate::model::profile::{Mu3Audio, Mu3Ini};
|
||||
|
||||
impl Mu3Ini {
|
||||
pub fn line_up(&self, game_path: impl AsRef<Path>) -> Result<()> {
|
||||
let file = game_path.as_ref().join("mu3.ini");
|
||||
pub fn line_up(&self, data_dir: impl AsRef<Path>, cfg_dir: impl AsRef<Path>) -> Result<()> {
|
||||
let file = cfg_dir.as_ref().join("mu3.ini");
|
||||
|
||||
if !file.exists() {
|
||||
std::fs::write(&file, "")?;
|
||||
@ -20,9 +20,26 @@ impl Mu3Ini {
|
||||
Mu3Audio::Excl2Ch => "2",
|
||||
};
|
||||
|
||||
ini.with_section(Some("Sound")).set("WasapiExclusive", value);
|
||||
ini.with_section(Some("Sound"))
|
||||
.set("WasapiExclusive", value)
|
||||
.set("SampleRate", self.sample_rate.to_string());
|
||||
}
|
||||
|
||||
if let Some(blacklist) = self.blacklist {
|
||||
ini.with_section(Some("Extra"))
|
||||
.set("BlacklistMin", blacklist.0.to_string())
|
||||
.set("BlacklistMax", blacklist.1.to_string());
|
||||
}
|
||||
|
||||
let cache_path = data_dir.as_ref().join("mu3-mods-cache");
|
||||
let cache_path = cache_path.to_str()
|
||||
.ok_or_else(|| anyhow!("Invalid cache path"))?;
|
||||
|
||||
ini.with_section(Some("Extra"))
|
||||
.set("GP", self.gp.to_string())
|
||||
.set("CacheDir", cache_path)
|
||||
.set("UnlockBonusTracks", crate::util::bool_to_01(self.enable_bonus_tracks));
|
||||
|
||||
ini.write_to_file(file)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -1,17 +1,26 @@
|
||||
use anyhow::Result;
|
||||
use std::collections::BTreeSet;
|
||||
use std::path::PathBuf;
|
||||
use crate::model::misc::Game;
|
||||
use crate::pkg::{PkgKey, Status};
|
||||
use crate::pkg_store::PackageStore;
|
||||
use crate::util;
|
||||
use crate::profiles::types::ProfilePaths;
|
||||
|
||||
pub async fn prepare_packages<'a>(p: &'a impl ProfilePaths, pkgs: &BTreeSet<PkgKey>, redo_bepinex: bool) -> Result<()> {
|
||||
pub async fn prepare_packages<'a>(p: &'a impl ProfilePaths, pkgs: &BTreeSet<PkgKey>, mut redo_bepinex: bool) -> Result<()> {
|
||||
log::debug!("begin prepare packages");
|
||||
|
||||
let pfx_dir = p.data_dir();
|
||||
let opt_dir = pfx_dir.join("option");
|
||||
|
||||
for m in pkgs {
|
||||
let (namespace, _) = m.split()?;
|
||||
if namespace == "local" {
|
||||
log::info!("package with the 'local' namespace enabled -- force refreshing");
|
||||
redo_bepinex = true;
|
||||
}
|
||||
}
|
||||
|
||||
if redo_bepinex {
|
||||
if pfx_dir.join("BepInEx").exists() {
|
||||
util::remove_dir_all(pfx_dir.join("BepInEx")).await?;
|
||||
@ -60,6 +69,7 @@ pub async fn prepare_packages<'a>(p: &'a impl ProfilePaths, pkgs: &BTreeSet<PkgK
|
||||
}
|
||||
|
||||
pub fn prepare_dlls(
|
||||
game: Game,
|
||||
enabled_pkgs: &BTreeSet<PkgKey>,
|
||||
store: &PackageStore,
|
||||
) -> Result<(Vec<PathBuf>, Vec<PathBuf>)> {
|
||||
@ -72,6 +82,16 @@ pub fn prepare_dlls(
|
||||
if let Some(game_dll) = &dlls.game {
|
||||
res_game.push(pkg.path().join(game_dll.clone()));
|
||||
}
|
||||
if let Some(game32_dll) = &dlls.game32 {
|
||||
if game.bitness() == 32 {
|
||||
res_game.push(pkg.path().join(game32_dll.clone()));
|
||||
}
|
||||
}
|
||||
if let Some(game64_dll) = &dlls.game64 {
|
||||
if game.bitness() == 64 {
|
||||
res_game.push(pkg.path().join(game64_dll.clone()));
|
||||
}
|
||||
}
|
||||
if let Some(amd_dll) = &dlls.amd {
|
||||
res_amd.push(pkg.path().join(amd_dll.clone()));
|
||||
}
|
||||
|
@ -5,30 +5,30 @@ use crate::{model::{misc::{ConfigHook, ConfigHookAime, ConfigHookAimeUnit, Confi
|
||||
use crate::pkg_store::PackageStore;
|
||||
|
||||
impl Segatools {
|
||||
pub fn fix(&mut self, store: &PackageStore) {
|
||||
macro_rules! remove_if_nonpresent {
|
||||
($item:expr,$key:expr,$emptyval:expr,$store:expr) => {
|
||||
if let Ok(pkg) = $store.get($key) {
|
||||
if pkg.loc.is_none() {
|
||||
$item = $emptyval;
|
||||
}
|
||||
} else {
|
||||
$item = $emptyval;
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn fix(&mut self, _store: &PackageStore) {
|
||||
// macro_rules! remove_if_nonpresent {
|
||||
// ($item:expr,$key:expr,$emptyval:expr,$store:expr) => {
|
||||
// if let Ok(pkg) = $store.get($key) {
|
||||
// if pkg.loc.is_none() {
|
||||
// $item = $emptyval;
|
||||
// }
|
||||
// } else {
|
||||
// $item = $emptyval;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if let Some(key) = &self.hook {
|
||||
remove_if_nonpresent!(self.hook, key, None, store);
|
||||
}
|
||||
if let IOSelection::Custom(key) = &self.io2 {
|
||||
remove_if_nonpresent!(self.io2, key, IOSelection::default(), store);
|
||||
}
|
||||
match &self.aime {
|
||||
Aime::AMNet(key) => remove_if_nonpresent!(self.aime, key, Aime::BuiltIn, store),
|
||||
Aime::Other(key) => remove_if_nonpresent!(self.aime, key, Aime::BuiltIn, store),
|
||||
_ => {},
|
||||
}
|
||||
// if let Some(key) = &self.hook {
|
||||
// remove_if_nonpresent!(self.hook, key, None, store);
|
||||
// }
|
||||
// if let IOSelection::Custom(key) = &self.io2 {
|
||||
// remove_if_nonpresent!(self.io2, key, IOSelection::default(), store);
|
||||
// }
|
||||
// match &self.aime {
|
||||
// Aime::AMNet(key) => remove_if_nonpresent!(self.aime, key, Aime::BuiltIn, store),
|
||||
// Aime::Other(key) => remove_if_nonpresent!(self.aime, key, Aime::BuiltIn, store),
|
||||
// _ => {},
|
||||
// }
|
||||
}
|
||||
pub fn load_from_ini(&mut self, ini: &Ini, config_dir: impl AsRef<Path>) -> Result<()> {
|
||||
log::debug!("loading sgt");
|
||||
|
@ -12,13 +12,18 @@ impl PatchFileVec {
|
||||
std::fs::create_dir(&path)?;
|
||||
}
|
||||
std::fs::write(path.join("builtin-chunithm.json5"), include_bytes!("../static/standard-chunithm.json5"))?;
|
||||
std::fs::write(path.join("builtin-amdaemon.json5"), include_bytes!("../static/standard-amdaemon.json5"))?;
|
||||
let mut res = Vec::new();
|
||||
for f in std::fs::read_dir(path)? {
|
||||
let f = f?;
|
||||
let f = f.path();
|
||||
res.push(
|
||||
serde_json5::from_str::<PatchFile>(&std::fs::read_to_string(f)?)?
|
||||
);
|
||||
let f = &f.path();
|
||||
match serde_json5::from_str::<PatchFile>(&std::fs::read_to_string(f)?) {
|
||||
Ok(parsed) => res.push(parsed),
|
||||
Err(e) => {
|
||||
log::error!("Error parsing {f:?}: {e}");
|
||||
anyhow::bail!("Error parsing {f:?}: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(PatchFileVec(res))
|
||||
}
|
||||
@ -26,20 +31,21 @@ impl PatchFileVec {
|
||||
pub fn find_patches(&self, target: impl AsRef<Path>) -> Result<Vec<Patch>> {
|
||||
let checksum = try_digest(target.as_ref())?;
|
||||
|
||||
let mut res = Vec::new();
|
||||
let mut res_patches = Vec::new();
|
||||
for pfile in &self.0 {
|
||||
for plist in &pfile.0 {
|
||||
log::debug!("checking {}", plist.sha256);
|
||||
if plist.sha256 == checksum {
|
||||
let this_hash = plist.sha256.to_ascii_lowercase();
|
||||
log::debug!("checking {}", this_hash);
|
||||
if this_hash == checksum {
|
||||
let mut cloned = plist.clone().patches;
|
||||
res.append(&mut cloned);
|
||||
res_patches.append(&mut cloned);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if res.len() == 0 {
|
||||
if res_patches.len() == 0 {
|
||||
log::warn!("no matching patchset for {:?} ({})", target.as_ref(), checksum);
|
||||
}
|
||||
Ok(res)
|
||||
Ok(res_patches)
|
||||
}
|
||||
}
|
@ -41,6 +41,8 @@ pub enum Status {
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize, Debug)]
|
||||
pub struct DLLs {
|
||||
pub game: Option<String>,
|
||||
pub game32: Option<String>,
|
||||
pub game64: Option<String>,
|
||||
pub amd: Option<String>
|
||||
}
|
||||
|
||||
@ -120,7 +122,7 @@ impl Package {
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn from_dir(dir: PathBuf, source: PackageSource) -> Result<Package> {
|
||||
pub async fn from_dir(dir: PathBuf, source: PackageSource) -> Result<(Package, Option<Vec<Game>>)> {
|
||||
let str = fs::read_to_string(dir.join("manifest.json")).await?;
|
||||
let mft: local::PackageManifest = serde_json::from_str(&str)?;
|
||||
|
||||
@ -133,7 +135,7 @@ impl Package {
|
||||
let status = Self::parse_status(&mft, &dir);
|
||||
let dependencies = Self::sanitize_deps(mft.dependencies);
|
||||
|
||||
Ok(Package {
|
||||
Ok((Package {
|
||||
namespace: Self::dir_to_namespace(&dir)?,
|
||||
name: mft.name.clone(),
|
||||
description: mft.description.clone(),
|
||||
@ -146,7 +148,7 @@ impl Package {
|
||||
}),
|
||||
rmt: None,
|
||||
source
|
||||
})
|
||||
}, mft.games))
|
||||
}
|
||||
|
||||
pub fn key(&self) -> PkgKey {
|
||||
@ -231,10 +233,12 @@ impl Package {
|
||||
if dir.as_ref().join("app").join("data").exists() {
|
||||
return Status::Unsupported;
|
||||
}
|
||||
return Status::OK(make_bitflags!(Feature::Mod), DLLs { game: None, amd: None });
|
||||
return Status::OK(make_bitflags!(Feature::Mod), DLLs { game: None, game32: None, game64: None, amd: None });
|
||||
} else {
|
||||
let mut flags = BitFlags::default();
|
||||
let mut game_dll = None;
|
||||
let mut game32_dll = None;
|
||||
let mut game64_dll = None;
|
||||
let mut amd_dll = None;
|
||||
for installer in &mft.installers {
|
||||
if let Some(serde_json::Value::String(id)) = installer.get("identifier") {
|
||||
@ -262,6 +266,16 @@ impl Package {
|
||||
flags |= Feature::Mod;
|
||||
game_dll = Some(path.to_owned());
|
||||
}
|
||||
if let Some(serde_json::Value::String(path)) = installer.get("dll-game32") {
|
||||
flags |= Feature::GameDLL;
|
||||
flags |= Feature::Mod;
|
||||
game32_dll = Some(path.to_owned());
|
||||
}
|
||||
if let Some(serde_json::Value::String(path)) = installer.get("dll-game64") {
|
||||
flags |= Feature::GameDLL;
|
||||
flags |= Feature::Mod;
|
||||
game64_dll = Some(path.to_owned());
|
||||
}
|
||||
if let Some(serde_json::Value::String(path)) = installer.get("dll-amdaemon") {
|
||||
flags |= Feature::AmdDLL;
|
||||
flags |= Feature::Mod;
|
||||
@ -272,8 +286,8 @@ impl Package {
|
||||
}
|
||||
}
|
||||
}
|
||||
log::debug!("{} parse result: {:?} {:?} {:?}", mft.name, flags, game_dll, amd_dll);
|
||||
Status::OK(flags, DLLs { game: game_dll, amd: amd_dll })
|
||||
log::debug!("{} parse result: {:?} {:?} {:?} {:?} {:?}", mft.name, flags, game_dll, game32_dll, game64_dll, amd_dll);
|
||||
Status::OK(flags, DLLs { game: game_dll, game32: game32_dll, game64: game64_dll, amd: amd_dll })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,7 +83,7 @@ impl PackageStore {
|
||||
|
||||
pub async fn reload_package(&mut self, key: PkgKey) {
|
||||
let dir = util::pkg_dir().join(&key.0);
|
||||
if let Ok(pkg) = Package::from_dir(dir, PackageSource::Rainy).await {
|
||||
if let Ok((pkg, _)) = Package::from_dir(dir, PackageSource::Rainy).await {
|
||||
self.update_nonremote(key, pkg);
|
||||
} else {
|
||||
log::error!("couldn't reload {}", key);
|
||||
@ -102,7 +102,13 @@ impl PackageStore {
|
||||
}
|
||||
|
||||
while let Some(res) = futures.join_next().await {
|
||||
if let Ok(Ok(pkg)) = res {
|
||||
if let Ok(Ok((pkg, locally_declared_games))) = res {
|
||||
if let Some(games) = locally_declared_games {
|
||||
self.meta_list.insert(pkg.key(), PackageListEntry {
|
||||
version: pkg.loc.as_ref().unwrap().version.clone(),
|
||||
games
|
||||
});
|
||||
}
|
||||
self.update_nonremote(pkg.key(), pkg);
|
||||
}
|
||||
}
|
||||
@ -126,7 +132,7 @@ impl PackageStore {
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
let response = reqwest::get(format!("https://rainy.patafour.zip/c/{game}/api/v1/package/")).await?;
|
||||
let response = reqwest::get(format!("https://www.rainycolor.org/c/{game}/api/v1/package/")).await?;
|
||||
|
||||
let reader = response
|
||||
.bytes_stream()
|
||||
@ -152,7 +158,6 @@ impl PackageStore {
|
||||
PackageListEntry {
|
||||
// from_rainy() is guaranteed to include rmt
|
||||
version: r.rmt.as_ref().unwrap().version.clone(),
|
||||
status: Status::Unchecked,
|
||||
games: vec![ game ],
|
||||
}
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
pub use types::{Profile, ProfileData, ProfileMeta, ProfilePaths, StartPayload};
|
||||
use std::{collections::{BTreeMap, BTreeSet}, path::{Path, PathBuf}};
|
||||
use crate::{model::{misc::Game, patch::{PatchFileVec, PatchSelection}, profile::{Aime, ChunithmKeyboard, IOSelection, Keyboard, Mu3Ini, OngekiKeyboard, ProfileModule}}, modules::{display_windows::DisplayInfo, package::prepare_packages}, pkg::PkgKey, pkg_store::PackageStore, util};
|
||||
use crate::{model::{misc::Game, patch::{PatchList, PatchSelection}, profile::{Aime, ChunithmKeyboard, IOSelection, Keyboard, Mu3Ini, OngekiKeyboard, ProfileModule}}, modules::package::prepare_packages, pkg::PkgKey, pkg_store::PackageStore, util};
|
||||
use tauri::Emitter;
|
||||
use std::process::Stdio;
|
||||
use crate::model::profile::BepInEx;
|
||||
@ -10,8 +10,23 @@ use std::fs::File;
|
||||
use tokio::process::Command;
|
||||
use tokio::task::JoinSet;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
use crate::modules::display_windows::DisplayInfo;
|
||||
|
||||
pub mod template;
|
||||
pub mod types;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub trait RawArg {
|
||||
fn raw_arg<S: AsRef<std::ffi::OsStr>>(&mut self, arg: S) -> &mut Command;
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
impl RawArg for Command {
|
||||
fn raw_arg<S: AsRef<std::ffi::OsStr>>(&mut self, arg: S) -> &mut Command {
|
||||
return self.arg::<S>(arg);
|
||||
}
|
||||
}
|
||||
|
||||
impl Profile {
|
||||
pub fn new(mut meta: ProfileMeta) -> Result<Self> {
|
||||
meta.name = fixed_name(&meta, true);
|
||||
@ -28,14 +43,14 @@ impl Profile {
|
||||
bepinex: if meta.game == Game::Ongeki { Some(BepInEx::default()) } else { None },
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
wine: crate::model::profile::Wine::default(),
|
||||
mu3_ini: if meta.game == Game::Ongeki { Some(Mu3Ini { audio: None, blacklist: None }) } else { None },
|
||||
mu3_ini: if meta.game == Game::Ongeki { Some(Mu3Ini::default()) } else { None },
|
||||
keyboard:
|
||||
if meta.game == Game::Ongeki {
|
||||
Some(Keyboard::Ongeki(OngekiKeyboard::default()))
|
||||
} else {
|
||||
Some(Keyboard::Chunithm(ChunithmKeyboard::default()))
|
||||
},
|
||||
patches: if meta.game == Game::Chunithm { Some(PatchSelection(BTreeMap::new())) } else { None }
|
||||
patches: Some(PatchSelection(BTreeMap::new()))
|
||||
},
|
||||
meta: meta.clone()
|
||||
};
|
||||
@ -67,6 +82,21 @@ impl Profile {
|
||||
data.sgt.io2 = IOSelection::Custom(io);
|
||||
data.sgt.io = None;
|
||||
}
|
||||
if let Some(ini) = &mut data.mu3_ini {
|
||||
if ini.audio.is_none() {
|
||||
ini.audio = Some(crate::model::profile::Mu3Audio::Shared);
|
||||
}
|
||||
if ini.blacklist.is_none() {
|
||||
ini.blacklist = Some((10000, 19999));
|
||||
}
|
||||
} else {
|
||||
data.mu3_ini = Some(Mu3Ini::default());
|
||||
}
|
||||
if data.patches.is_none() {
|
||||
data.patches = Some(PatchSelection(BTreeMap::new()));
|
||||
}
|
||||
|
||||
Self::load_existing_mu3_ini(&data, &ProfileMeta { game, name: name.clone() })?;
|
||||
}
|
||||
if game == Game::Chunithm {
|
||||
if data.keyboard.is_none() {
|
||||
@ -160,6 +190,7 @@ impl Profile {
|
||||
self.data.patches = source.patches;
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn prepare_display(&self) -> Result<Option<DisplayInfo>> {
|
||||
let info = match &self.data.display {
|
||||
None => None,
|
||||
@ -168,7 +199,7 @@ impl Profile {
|
||||
|
||||
Ok(info)
|
||||
}
|
||||
pub async fn line_up(&self, pkg_hash: String, refresh: bool, patch_files: &PatchFileVec) -> Result<()> {
|
||||
pub async fn line_up(&self, pkg_hash: String, refresh: bool, patchlists_enabled: Vec<&PatchList>) -> Result<()> {
|
||||
if !self.data_dir().exists() {
|
||||
tokio::fs::create_dir(self.data_dir()).await?;
|
||||
}
|
||||
@ -203,13 +234,13 @@ impl Profile {
|
||||
}
|
||||
|
||||
if let Some(mu3ini) = &self.data.mu3_ini {
|
||||
mu3ini.line_up(&self.data.sgt.target.parent().unwrap())?;
|
||||
mu3ini.line_up(&self.data_dir(), &self.config_dir())?;
|
||||
}
|
||||
|
||||
if let Some(patches) = &self.data.patches {
|
||||
futures::try_join!(
|
||||
patches.render_to_file("amdaemon.exe", patch_files, self.data_dir().join("patch-amd.mph")),
|
||||
patches.render_to_file("chusanApp.exe", patch_files, self.data_dir().join("patch-game.mph"))
|
||||
patches.render_to_file("amdaemon.exe", &patchlists_enabled, self.data_dir().join("patch-amd.mph")),
|
||||
patches.render_to_file("chusanApp.exe", &patchlists_enabled, self.data_dir().join("patch-game.mph"))
|
||||
)?;
|
||||
}
|
||||
|
||||
@ -236,8 +267,8 @@ impl Profile {
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
game_builder = Command::new(&self.wine.runtime);
|
||||
amd_builder = Command::new(&self.wine.runtime);
|
||||
game_builder = Command::new(&self.data.wine.runtime);
|
||||
amd_builder = Command::new(&self.data.wine.runtime);
|
||||
|
||||
game_builder.arg(sgt_dir.join(self.meta.game.inject_exe()));
|
||||
amd_builder.arg("cmd.exe");
|
||||
@ -283,6 +314,14 @@ impl Profile {
|
||||
"ONGEKI_LANG_PATH",
|
||||
self.data_dir().join("lang"),
|
||||
)
|
||||
.env(
|
||||
"MU3_MODS_CONFIG_PATH",
|
||||
self.config_dir().join("mu3.ini"),
|
||||
)
|
||||
.env(
|
||||
"STARTLINER",
|
||||
"1"
|
||||
)
|
||||
.current_dir(&exe_dir)
|
||||
.raw_arg("-d")
|
||||
.raw_arg("-k")
|
||||
@ -325,8 +364,8 @@ impl Profile {
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
amd_builder.env("WINEPREFIX", &self.wine.prefix);
|
||||
game_builder.env("WINEPREFIX", &self.wine.prefix);
|
||||
amd_builder.env("WINEPREFIX", &self.data.wine.prefix);
|
||||
game_builder.env("WINEPREFIX", &self.data.wine.prefix);
|
||||
}
|
||||
|
||||
let amd_log = File::create(self.data_dir().join("amdaemon.exe.log"))?;
|
||||
@ -403,6 +442,19 @@ impl Profile {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
fn load_existing_mu3_ini(data: &ProfileData, meta: &ProfileMeta) -> Result<()> {
|
||||
if let Some(parent) = data.sgt.target.parent() {
|
||||
let mu3_ini_target_path = parent.join("mu3.ini");
|
||||
let mu3_ini_profile_path = util::profile_config_dir(meta.game, &meta.name).join("mu3.ini");
|
||||
log::debug!("mu3.ini paths: {:?} {:?}", mu3_ini_target_path, mu3_ini_profile_path);
|
||||
if mu3_ini_target_path.exists() && !mu3_ini_profile_path.exists() {
|
||||
std::fs::copy(&mu3_ini_target_path, &mu3_ini_profile_path)?;
|
||||
log::info!("copied mu3.ini from {:?}", &mu3_ini_target_path);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ProfilePaths for Profile {
|
||||
|
90
rust/src/profiles/template.rs
Normal file
90
rust/src/profiles/template.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use std::{fs::File, io::{Read, Write}, path::PathBuf};
|
||||
use zip::{write::FileOptions, ZipArchive, ZipWriter};
|
||||
use crate::util;
|
||||
use super::{Profile, ProfilePaths};
|
||||
|
||||
impl Profile {
|
||||
fn find_template_json(archive: &mut ZipArchive<File>) -> anyhow::Result<String> {
|
||||
if let Ok(mut file) = archive.by_name("template.json") {
|
||||
let mut contents = Vec::new();
|
||||
file.read_to_end(&mut contents)?;
|
||||
Ok(String::from_utf8(contents)?)
|
||||
} else {
|
||||
anyhow::bail!("invalid template: no template.json found")
|
||||
}
|
||||
}
|
||||
pub fn import(path: PathBuf) -> anyhow::Result<()> {
|
||||
let file = File::open(path)?;
|
||||
let mut archive = ZipArchive::new(file)?;
|
||||
|
||||
match Self::find_template_json(&mut archive) {
|
||||
Ok(raw_p) => {
|
||||
let p = serde_json::from_str::<Profile>(&raw_p)?;
|
||||
let dir = util::config_dir().join(format!("profile-{}-{}", &p.meta.game, &p.meta.name));
|
||||
if dir.exists() {
|
||||
anyhow::bail!("profile {} already exists", &p.meta.name);
|
||||
}
|
||||
std::fs::create_dir(&dir)?;
|
||||
archive.extract(&dir)?;
|
||||
std::fs::remove_file(dir.join("template.json"))?;
|
||||
std::fs::write(dir.join("profile.json"), serde_json::to_string_pretty(&p.data)?)?;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn export(&self, export_keychip: bool, extra_files: Vec<String>) -> anyhow::Result<()> {
|
||||
let mut prf = self.clone();
|
||||
|
||||
let dir = util::config_dir().join("exports");
|
||||
|
||||
if !dir.exists() {
|
||||
std::fs::create_dir(&dir)?;
|
||||
}
|
||||
|
||||
let path = dir.join(format!("{}-{}-template.zip", &self.meta.game, &self.meta.name));
|
||||
|
||||
{
|
||||
let sgt = &mut prf.data.sgt;
|
||||
sgt.target = PathBuf::new();
|
||||
if sgt.amfs.is_absolute() {
|
||||
sgt.amfs = PathBuf::new();
|
||||
}
|
||||
if sgt.option.is_absolute() {
|
||||
sgt.option = PathBuf::new();
|
||||
}
|
||||
if sgt.appdata.is_absolute() {
|
||||
sgt.appdata = PathBuf::new();
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let network = &mut prf.data.network;
|
||||
if network.local_path.is_absolute() {
|
||||
network.local_path = PathBuf::new();
|
||||
}
|
||||
if !export_keychip {
|
||||
network.keychip = String::new();
|
||||
}
|
||||
}
|
||||
|
||||
let file = File::create(&path)?;
|
||||
let mut zip = ZipWriter::new(file);
|
||||
let options: FileOptions<'_, ()> = FileOptions::default();
|
||||
zip.start_file("template.json", options)?;
|
||||
zip.write_all(&serde_json::to_string_pretty(&prf)?.as_bytes())?;
|
||||
|
||||
for file in extra_files {
|
||||
log::debug!("extra file: {file}");
|
||||
zip.start_file(&file, options)?;
|
||||
zip.write_all(&std::fs::read(self.config_dir().join(file))?)?;
|
||||
}
|
||||
|
||||
zip.finish()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -152,6 +152,7 @@ impl PathStr for PathBuf {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn bool_to_01(val: bool) -> &'static str {
|
||||
return if val { "1" } else { "0" }
|
||||
}
|
||||
@ -199,7 +200,7 @@ pub fn create_shortcut(
|
||||
obj.SetDescription(&format!("{} – {} (STARTLINER)", &meta.game.print(), &meta.name))?;
|
||||
obj.SetArguments(&format!("--start --game {} --profile {}", &meta.game, &meta.name))?;
|
||||
obj.SetIconLocation(
|
||||
target_dir.join(format!("icon-{}.ico", &meta.game)).to_str().ok_or_else(|| anyhow!("Illegal icon path"))?,
|
||||
target_dir.join(format!("icon-{}.ico", &meta.game)).to_str().ok_or_else(|| anyhow!("Illegal icon path"))?,
|
||||
0
|
||||
)?;
|
||||
|
||||
|
@ -64,19 +64,4 @@ controllerLedOutputOpeNITHM=0
|
||||
; [60]-[62]: right side partition LEDs
|
||||
;
|
||||
; Board 2 is the slider and has 31 LEDs:
|
||||
; [0]-[31]: slider LEDs right to left BRG, alternating between keys and dividers
|
||||
|
||||
|
||||
; -----------------------------------------------------------------------------
|
||||
; Custom IO settings
|
||||
; -----------------------------------------------------------------------------
|
||||
|
||||
[chuniio]
|
||||
; Uncomment this if you have custom chuniio implementation comprised of a single 32bit DLL.
|
||||
; (will use chu2to3 engine internally)
|
||||
;path=
|
||||
|
||||
; Uncomment both of these if you have custom chuniio implementation comprised of two DLLs.
|
||||
; x86 chuniio to path32, x64 to path64. Both are necessary.
|
||||
;path32=
|
||||
;path64=
|
||||
; [0]-[31]: slider LEDs right to left BRG, alternating between keys and dividers
|
105
rust/static/standard-amdaemon.json5
Normal file
105
rust/static/standard-amdaemon.json5
Normal file
@ -0,0 +1,105 @@
|
||||
[
|
||||
{
|
||||
// CHUNITHM Luminous+
|
||||
filename: 'amdaemon.exe',
|
||||
version: '2.25.00',
|
||||
sha256: '00FB867D1EE821033101B8773FAC116A45DF1939D23C38E9DAFC9B86CD5A3777',
|
||||
patches: [
|
||||
{
|
||||
id: 'standard-localhost',
|
||||
name: "Allow 127.0.0.1/localhost as the network server",
|
||||
patches: [
|
||||
{ offset: 0x6E28A4, off: [0x31, 0x32, 0x37, 0x2F], on: [0x30, 0x2F, 0x38, 0x00] },
|
||||
{ offset: 0x3C94C4, off: [0xFF, 0x15, 0xC6, 0x2F, 0x1B, 0x00, 0x8B], on: [0x33, 0xC0, 0x48, 0x83, 0xC4, 0x28, 0xC3] }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'standard-credit-freeze',
|
||||
name: "Infinite credits",
|
||||
patches: [
|
||||
{ offset: 0x2BBBC8, off: [0x28], on: [0x08] }
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
// CHUNITHM Verse
|
||||
filename: 'amdaemon.exe',
|
||||
version: '2.30.00',
|
||||
sha256: 'd4809220578374865370e31c541ed6e406b854d8c26cfe7464c2c15145113bfd',
|
||||
patches: [
|
||||
{
|
||||
id: 'standard-localhost',
|
||||
name: 'Allow 127.0.0.1/localhost as the network server',
|
||||
patches: [
|
||||
{
|
||||
offset: 0x6e1ca4,
|
||||
off: [0x31, 0x32, 0x37, 0x2f],
|
||||
on: [0x30, 0x2f, 0x38, 0x00],
|
||||
},
|
||||
{
|
||||
offset: 0x3c88c4,
|
||||
off: [0xff, 0x15, 0xc6, 0x2f, 0x1b, 0x00, 0x8b],
|
||||
on: [0x33, 0xc0, 0x48, 0x83, 0xc4, 0x28, 0xc3],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'standard-credit-freeze',
|
||||
name: 'Credit freeze',
|
||||
tooltip: 'Prevents credits from being used. At least one credit must be available to start the game or purchase premium tickets.',
|
||||
patches: [{ offset: 0x2bafc8, off: [0x28], on: [0x08] }],
|
||||
},
|
||||
{
|
||||
id: 'standard-openssl-fix',
|
||||
name: 'OpenSSL SHA crash bug fix',
|
||||
tooltip: 'Fix crashes on 10th generation and newer Intel CPUs',
|
||||
patches: [
|
||||
{ offset: 0x4d4a43, off: [0x48], on: [0x4c] },
|
||||
{ offset: 0x4d4a4b, off: [0x48], on: [0x49] },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
// Ongeki
|
||||
filename: "amdaemon.exe",
|
||||
version: "46d47eab",
|
||||
sha256: '962C76331306D0839AFD40808EA99D83E651D39C4708C448ADE0C77E8BC0A1B0',
|
||||
patches: [
|
||||
{
|
||||
id: 'standard-localhost',
|
||||
name: "Allow 127.0.0.1/localhost as the network server",
|
||||
patches: [
|
||||
{
|
||||
offset: 0x69042C,
|
||||
off: [0x31, 0x32, 0x37, 0x2F],
|
||||
on: [0x30, 0x2F, 0x38, 0x00]
|
||||
},
|
||||
{
|
||||
offset: 0x3A0E34,
|
||||
off: [0xFF, 0x15, 0x86, 0xD5, 0x19, 0x00, 0x8B],
|
||||
on: [0x33, 0xC0, 0x48, 0x83, 0xC4, 0x28, 0xC3]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'standard-credit-freeze',
|
||||
name: "Credit freeze",
|
||||
tooltip: "Prevents credits from being used. At least one credit must be available to start the game or purchase premium tickets.",
|
||||
patches: [
|
||||
{ offset: 0x2AB4E8, off: [0x28], on: [0x08] }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'standard-openssl-fix',
|
||||
name: "OpenSSL SHA crash bug fix",
|
||||
tooltip: "Fixes crashing on 10th generation and newer Intel CPUs",
|
||||
patches: [
|
||||
{ offset: 0x4A4C43, off: [0x48], on: [0x4C] },
|
||||
{ offset: 0x4A4C4B, off: [0x48], on: [0x49] }
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
@ -1,4 +1,132 @@
|
||||
[
|
||||
{
|
||||
filename: 'chusanApp.exe',
|
||||
version: '2.26.00',
|
||||
sha256: 'AD2DCC02CE52B3FFF24A2919F8617854581DD2E2C0378EA13D84438FCCA2D522',
|
||||
patches: [
|
||||
{
|
||||
id: 'standard-shared-audio',
|
||||
name: "Force shared audio mode, system audio sample rate must be 48000Hz",
|
||||
tooltip: "Improves compatibility, but may increase latency",
|
||||
patches: [
|
||||
{offset: 0xF233DA, off: [0x01], on: [0x00]}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'standard-2ch',
|
||||
name: "Force 2 channel audio output",
|
||||
tooltip: "May cause bass overload",
|
||||
patches: [
|
||||
{offset: 0xF234B1, off: [0x75, 0x3f], on: [0x90, 0x90]}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'standard-song-timer',
|
||||
name: "Disable song select timer",
|
||||
patches: [
|
||||
{offset: 0xA03916, off: [0x74], on: [0xeb]}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'standard-map-timer',
|
||||
name: "Map selection timer",
|
||||
tooltip: "If set to negative, the timer becomes 968 + value (e.g. 968 + -1 = 967)",
|
||||
type: "number",
|
||||
offset: 0x965B37,
|
||||
default: 30,
|
||||
size: 1,
|
||||
min: -128,
|
||||
max: 127,
|
||||
},
|
||||
{
|
||||
id: 'standard-ticket-timer',
|
||||
name: "Ticket selection timer",
|
||||
tooltip: "If set to negative, the timer becomes 968 + value (e.g. 968 + -1 = 967)",
|
||||
type: "number",
|
||||
offset: 0x9592C2,
|
||||
default: 60,
|
||||
size: 1,
|
||||
min: -128,
|
||||
max: 127,
|
||||
},
|
||||
{
|
||||
id: 'standard-course-timer',
|
||||
name: "Course selection timer",
|
||||
tooltip: "If set to negative, the timer becomes 968 + value (e.g. 968 + -1 = 967)",
|
||||
type: "number",
|
||||
offset: 0xA0EADB,
|
||||
default: 30,
|
||||
size: 1,
|
||||
min: -128,
|
||||
max: 127,
|
||||
},
|
||||
{
|
||||
id: 'standard-unlimited-tracks',
|
||||
name: "Unlimited maximum tracks",
|
||||
tooltip: "Must check to play more than 7 tracks per credit",
|
||||
patches: [
|
||||
{offset: 0x71E2E0, off: [0xf0], on: [0xc0]}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'standard-maximum-tracks',
|
||||
type: "number",
|
||||
name: "Maximum tracks",
|
||||
offset: 0x3980C1,
|
||||
default: 3,
|
||||
size: 1,
|
||||
min: 3,
|
||||
max: 12
|
||||
},
|
||||
{
|
||||
id: 'standard-no-encryption',
|
||||
name: "No encryption",
|
||||
tooltip: "Will also disable TLS",
|
||||
patches: [
|
||||
{offset: 0x1DE29E8, off: [0xE1], on: [0x00]},
|
||||
{offset: 0x1DE29EC, off: [0xE1], on: [0x00]}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'standard-no-tls',
|
||||
name: "No TLS",
|
||||
tooltip: "Title server workaround",
|
||||
patches: [
|
||||
{offset: 0xF06447, off: [0x80], on: [0x00]}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'standard-head-to-head',
|
||||
name: "Patch for head-to-head play",
|
||||
tooltip: "Fix infinite sync while trying to connect to head to head play",
|
||||
patches: [
|
||||
{offset: 0x6533A3, off: [0x01], on: [0x00]}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'standard-bypass-1080p',
|
||||
name: "Bypass 1080p monitor check",
|
||||
patches: [
|
||||
{offset: 0x1CCBF, off: [0x81, 0xbc, 0x24, 0xb8, 0x02, 0x00, 0x00, 0x80, 0x07, 0x00, 0x00, 0x75, 0x1f, 0x81, 0xbc, 0x24, 0xbc, 0x02, 0x00, 0x00, 0x38, 0x04, 0x00, 0x00, 0x75, 0x12], on: [0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90]}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'standard-bypass-120hz',
|
||||
name: "Bypass 120Hz monitor check",
|
||||
patches: [
|
||||
{offset: 0x1CCB1, off: [0x85, 0xc0], on: [0xeb, 0x30]}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'standard-force-free-play-text',
|
||||
name: "Force FREE PLAY credit text",
|
||||
tooltip: "Replaces the credit count with FREE PLAY",
|
||||
patches: [
|
||||
{offset: 0x3875A4, off: [0x3c, 0x01], on: [0x38, 0xc0]}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
filename: 'chusanApp.exe',
|
||||
version: '2.30.00',
|
||||
@ -176,43 +304,25 @@
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
filename: 'amdaemon.exe',
|
||||
version: '2.30.00',
|
||||
sha256: 'd4809220578374865370e31c541ed6e406b854d8c26cfe7464c2c15145113bfd',
|
||||
patches: [
|
||||
{
|
||||
id: 'standard-localhost',
|
||||
name: 'Allow 127.0.0.1/localhost as the network server',
|
||||
patches: [
|
||||
{
|
||||
offset: 0x6e1ca4,
|
||||
off: [0x31, 0x32, 0x37, 0x2f],
|
||||
on: [0x30, 0x2f, 0x38, 0x00],
|
||||
},
|
||||
{
|
||||
offset: 0x3c88c4,
|
||||
off: [0xff, 0x15, 0xc6, 0x2f, 0x1b, 0x00, 0x8b],
|
||||
on: [0x33, 0xc0, 0x48, 0x83, 0xc4, 0x28, 0xc3],
|
||||
},
|
||||
],
|
||||
id: 'standard-custom-free-play-length',
|
||||
type: 'number',
|
||||
name: 'Custom FREE PLAY text length',
|
||||
tooltip: 'Changes the length of the text displayed when Force FREE PLAY credit text is enabled',
|
||||
danger: 'If this is longer than 11 characters, \"Force FREE PLAY credit text\" MUST be enabled.',
|
||||
offset: 0x3875A9,
|
||||
size: 1,
|
||||
default: 9,
|
||||
min: 0,
|
||||
max: 27,
|
||||
},
|
||||
{
|
||||
id: 'standard-credit-freeze',
|
||||
name: 'Credit freeze',
|
||||
tooltip: 'Prevents credits from being used. At least one credit must be available to start the game or purchase premium tickets.',
|
||||
patches: [{ offset: 0x2bafc8, off: [0x28], on: [0x08] }],
|
||||
},
|
||||
{
|
||||
id: 'standard-openssl-fix',
|
||||
name: 'OpenSSL SHA crash bug fix',
|
||||
tooltip: 'Fix crashes on 10th generation and newer Intel CPUs',
|
||||
patches: [
|
||||
{ offset: 0x4d4a43, off: [0x48], on: [0x4c] },
|
||||
{ offset: 0x4d4a4b, off: [0x48], on: [0x49] },
|
||||
],
|
||||
id: 'standard-custom-free-play-text',
|
||||
type: 'hex',
|
||||
name: 'Custom FREE PLAY text',
|
||||
tooltip: 'Replace the FREE PLAY text when using Infinite credits',
|
||||
offset: 0x1A5DFB4,
|
||||
off: [0x46, 0x52, 0x45, 0x45, 0x20, 0x50, 0x4c, 0x41, 0x59],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "STARTLINER",
|
||||
"version": "0.11.0",
|
||||
"version": "0.18.2",
|
||||
"identifier": "zip.patafour.startliner",
|
||||
"build": {
|
||||
"beforeDevCommand": "bun run dev",
|
||||
|
@ -20,15 +20,16 @@ import OptionList from './OptionList.vue';
|
||||
import PatchList from './PatchList.vue';
|
||||
import ProfileList from './ProfileList.vue';
|
||||
import StartButton from './StartButton.vue';
|
||||
import { invoke } from '../invoke';
|
||||
import {
|
||||
useClientStore,
|
||||
useGeneralStore,
|
||||
usePkgStore,
|
||||
usePrfStore,
|
||||
} from '../stores';
|
||||
import { Dirs } from '../types';
|
||||
import { messageSplit, shouldPreferDark } from '../util';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
document.documentElement.classList.toggle('use-dark-mode', shouldPreferDark());
|
||||
|
||||
@ -37,10 +38,10 @@ const prf = usePrfStore();
|
||||
const general = useGeneralStore();
|
||||
const client = useClientStore();
|
||||
|
||||
client.load();
|
||||
|
||||
pkg.setupListeners();
|
||||
|
||||
const currentTab: Ref<'users' | 'loc' | 'patches' | 'rmt' | 'cfg' | 'info'> =
|
||||
ref('users');
|
||||
const pkgSearchTerm = ref('');
|
||||
|
||||
const isProfileDisabled = computed(() => prf.current === null);
|
||||
@ -56,17 +57,11 @@ listen<undefined>('update-end', (_) => {
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
invoke('list_directories').then((d) => {
|
||||
general.dirs = d as Dirs;
|
||||
client.load();
|
||||
});
|
||||
|
||||
const fetch_promise = pkg.fetch(true);
|
||||
|
||||
await Promise.all([prf.reloadList(), prf.reload()]);
|
||||
|
||||
if (prf.current !== null) {
|
||||
currentTab.value = 'loc';
|
||||
await pkg.reloadAll();
|
||||
}
|
||||
|
||||
@ -211,34 +206,34 @@ listen<DownloadingStatus>('download-progress', (event) => {
|
||||
|
||||
<Tabs
|
||||
lazy
|
||||
:value="currentTab"
|
||||
:value="client.currentTab"
|
||||
v-on:update:value="
|
||||
(value) => {
|
||||
currentTab = value as any;
|
||||
client.currentTab = value as string;
|
||||
client.save();
|
||||
}
|
||||
"
|
||||
class="h-screen"
|
||||
>
|
||||
<div class="fixed w-full flex z-100">
|
||||
<TabList class="grow" :show-navigators="false">
|
||||
<Tab value="users"><div class="pi pi-users"></div></Tab>
|
||||
<Tab value="users"><div class="pi pi-home"></div></Tab>
|
||||
<Tab
|
||||
:disabled="
|
||||
isProfileDisabled || pkg.networkStatus !== 'online'
|
||||
"
|
||||
value="rmt"
|
||||
><div class="pi pi-download"></div
|
||||
></Tab>
|
||||
<Tab :disabled="isProfileDisabled" value="loc"
|
||||
><div class="pi pi-box"></div
|
||||
></Tab>
|
||||
<Tab
|
||||
v-if="
|
||||
prf.current?.meta.game === 'chunithm' &&
|
||||
prf.current.data.sgt.target.length > 0
|
||||
"
|
||||
v-if="(prf.current?.data.sgt.target.length ?? 0) > 0"
|
||||
value="patches"
|
||||
><div class="pi pi-ticket"></div
|
||||
></Tab>
|
||||
<Tab
|
||||
v-if="pkg.networkStatus === 'online'"
|
||||
:disabled="isProfileDisabled"
|
||||
value="rmt"
|
||||
><div class="pi pi-download"></div
|
||||
></Tab>
|
||||
|
||||
<Tab :disabled="isProfileDisabled" value="cfg"
|
||||
><div class="pi pi-cog"></div
|
||||
></Tab>
|
||||
@ -251,17 +246,21 @@ listen<DownloadingStatus>('download-progress', (event) => {
|
||||
<div class="flex gap-4">
|
||||
<div
|
||||
class="flex"
|
||||
v-if="['loc', 'rmt', 'cfg'].includes(currentTab)"
|
||||
v-if="
|
||||
['loc', 'rmt', 'cfg'].includes(
|
||||
client.currentTab
|
||||
)
|
||||
"
|
||||
>
|
||||
<InputIcon class="self-center mr-2">
|
||||
<i class="pi pi-search" />
|
||||
</InputIcon>
|
||||
<InputText
|
||||
v-if="currentTab === 'cfg'"
|
||||
v-if="client.currentTab === 'cfg'"
|
||||
style="min-width: 0; width: 25dvw"
|
||||
class="self-center"
|
||||
size="small"
|
||||
placeholder="Search"
|
||||
:placeholder="t('search')"
|
||||
v-model="general.cfgSearchTerm"
|
||||
/>
|
||||
<InputText
|
||||
@ -269,7 +268,7 @@ listen<DownloadingStatus>('download-progress', (event) => {
|
||||
style="min-width: 0; width: 25dvw"
|
||||
class="self-center"
|
||||
size="small"
|
||||
placeholder="Search"
|
||||
:placeholder="t('search')"
|
||||
v-model="pkgSearchTerm"
|
||||
/>
|
||||
</div>
|
||||
@ -307,19 +306,19 @@ listen<DownloadingStatus>('download-progress', (event) => {
|
||||
</TabList>
|
||||
</div>
|
||||
<TabPanels class="w-full grow mt-[3rem]">
|
||||
<TabPanel value="loc">
|
||||
<TabPanel value="loc" v-if="!isProfileDisabled">
|
||||
<ModList :search="pkgSearchTerm" />
|
||||
</TabPanel>
|
||||
<TabPanel value="rmt">
|
||||
<TabPanel value="rmt" v-if="!isProfileDisabled">
|
||||
<ModStore :search="pkgSearchTerm" />
|
||||
</TabPanel>
|
||||
<TabPanel value="cfg">
|
||||
<TabPanel value="cfg" v-if="!isProfileDisabled">
|
||||
<OptionList />
|
||||
</TabPanel>
|
||||
<TabPanel value="users">
|
||||
<ProfileList />
|
||||
</TabPanel>
|
||||
<TabPanel value="patches">
|
||||
<TabPanel value="patches" v-if="!isProfileDisabled">
|
||||
<PatchList
|
||||
v-if="
|
||||
pkg.hasLocal('mempatcher-mempatcher') &&
|
||||
@ -350,7 +349,12 @@ listen<DownloadingStatus>('download-progress', (event) => {
|
||||
<InfoPage />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
<div v-if="currentTab === 'users' || currentTab === 'info'">
|
||||
<div
|
||||
v-if="
|
||||
client.currentTab === 'users' ||
|
||||
client.currentTab === 'info'
|
||||
"
|
||||
>
|
||||
<img
|
||||
v-if="prf.current?.meta.game === 'ongeki'"
|
||||
src="/sticker-ongeki.svg"
|
||||
|
@ -12,7 +12,7 @@ invoke('get_changelog').then((s) => (changelog.value = s as string));
|
||||
|
||||
<template>
|
||||
<h1>About</h1>
|
||||
STARTLINER is a simple launcher, configuration tool and mod manager for
|
||||
STARTLINER is a launcher, configuration tool and mod manager for
|
||||
O.N.G.E.K.I. and CHUNITHM.
|
||||
<h1>Changelog</h1>
|
||||
<ScrollPanel style="height: 200px">
|
||||
|
@ -4,6 +4,9 @@ import InputText from 'primevue/inputtext';
|
||||
import { fromKeycode, toKeycode } from '../keyboard';
|
||||
import { usePrfStore } from '../stores';
|
||||
import { OngekiButtons } from '../types';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const prf = usePrfStore();
|
||||
|
||||
@ -61,6 +64,10 @@ const handleKey = (
|
||||
}
|
||||
}
|
||||
|
||||
if (event.code === 'Escape') {
|
||||
keycode = 0;
|
||||
}
|
||||
|
||||
if (index !== undefined) {
|
||||
data[button][index] = keycode;
|
||||
} else {
|
||||
@ -160,13 +167,24 @@ const fontSize = computed(() => {
|
||||
<InputText
|
||||
:style="{
|
||||
width: small ? '2.8rem' : '5rem',
|
||||
height: small ? '2.8rem' : tall ? '10rem' : '5rem',
|
||||
height:
|
||||
small && tall
|
||||
? '5rem'
|
||||
: small
|
||||
? '2.8rem'
|
||||
: tall
|
||||
? '10rem'
|
||||
: '5rem',
|
||||
fontSize,
|
||||
backgroundColor: color,
|
||||
}"
|
||||
unstyled
|
||||
class="text-center buttoninputtext"
|
||||
v-tooltip="tooltip ? `${tooltip}: ${modelValue}` : undefined"
|
||||
v-tooltip="
|
||||
tooltip
|
||||
? `${tooltip}: ${modelValue} ${tooltip.startsWith('ir') ? `\n${t('cfg.keyboard.irTooltip')}` : ''}`
|
||||
: undefined
|
||||
"
|
||||
@contextmenu.prevent="() => {}"
|
||||
@keydown="(ev: KeyboardEvent) => handleKey(button, ev, index)"
|
||||
@mousedown="
|
||||
|
@ -1,61 +1,312 @@
|
||||
<script setup lang="ts">
|
||||
import { Ref, computed, ref } from 'vue';
|
||||
import Button from 'primevue/button';
|
||||
import Dialog from 'primevue/dialog';
|
||||
import Fieldset from 'primevue/fieldset';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import MultiSelect from 'primevue/multiselect';
|
||||
import SelectButton from 'primevue/selectbutton';
|
||||
import ToggleSwitch from 'primevue/toggleswitch';
|
||||
import { emit } from '@tauri-apps/api/event';
|
||||
import ModListEntry from './ModListEntry.vue';
|
||||
import ModTitlecard from './ModTitlecard.vue';
|
||||
import { invoke } from '../invoke';
|
||||
import { usePkgStore, usePrfStore } from '../stores';
|
||||
import { Package } from '../types';
|
||||
import { useClientStore, usePkgStore, usePrfStore } from '../stores';
|
||||
import { Feature, Game, Package } from '../types';
|
||||
import { pkgKey } from '../util';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
search: String,
|
||||
});
|
||||
|
||||
const pkgs = usePkgStore();
|
||||
const client = useClientStore();
|
||||
const prf = usePrfStore();
|
||||
const empty = ref(false);
|
||||
const gameSublist: Ref<string[]> = ref([]);
|
||||
|
||||
invoke('get_game_packages', {
|
||||
game: prf.current?.meta.game,
|
||||
}).then((list) => {
|
||||
gameSublist.value = list as string[];
|
||||
const loadPackages = () => {
|
||||
invoke('get_game_packages', {
|
||||
game: prf.current?.meta.game ?? null,
|
||||
}).then((list) => {
|
||||
gameSublist.value = list as string[];
|
||||
});
|
||||
};
|
||||
|
||||
loadPackages();
|
||||
|
||||
const allCategories = computed(() => {
|
||||
const res = new Set<string>();
|
||||
for (const pkg of pkgs.allLocal) {
|
||||
for (const cat of pkg.rmt?.categories ?? []) {
|
||||
res.add(cat);
|
||||
}
|
||||
}
|
||||
return [...res.values()].sort((a, b) => a.localeCompare(b));
|
||||
});
|
||||
|
||||
const group = computed(() => {
|
||||
const res = Object.assign(
|
||||
{},
|
||||
Object.groupBy(
|
||||
pkgs.allLocal
|
||||
.filter((p) => gameSublist.value.includes(pkgKey(p)))
|
||||
.filter(
|
||||
(p) =>
|
||||
props.search === undefined ||
|
||||
p.name
|
||||
.toLowerCase()
|
||||
.includes(props.search.toLowerCase()) ||
|
||||
p.namespace
|
||||
.toLowerCase()
|
||||
.includes(props.search.toLowerCase())
|
||||
)
|
||||
.sort((p1, p2) => p1.namespace.localeCompare(p2.namespace))
|
||||
.sort((p1, p2) => p1.name.localeCompare(p2.name)),
|
||||
({ namespace }) => namespace
|
||||
)
|
||||
);
|
||||
empty.value = Object.keys(res).length === 0;
|
||||
const local = computed(() => {
|
||||
return pkgs.allLocal
|
||||
.filter((p) => gameSublist.value.includes(pkgKey(p)))
|
||||
.filter((p) => p.namespace === 'local');
|
||||
});
|
||||
|
||||
const groupedList = computed(() => {
|
||||
const searchedPkgs = pkgs.allLocal
|
||||
.filter((p) => gameSublist.value.includes(pkgKey(p)))
|
||||
.filter((p) => p.namespace !== 'local')
|
||||
.filter(
|
||||
(p) =>
|
||||
props.search === undefined ||
|
||||
p.name.toLowerCase().includes(props.search.toLowerCase()) ||
|
||||
p.namespace.toLowerCase().includes(props.search.toLowerCase())
|
||||
);
|
||||
|
||||
let grouped;
|
||||
if (client.pkgListMode === 'namespace') {
|
||||
grouped = Object.groupBy(searchedPkgs, ({ namespace }) => namespace);
|
||||
} else if (client.pkgListMode === 'type') {
|
||||
grouped = {
|
||||
standard: [] as Package[],
|
||||
native: [] as Package[],
|
||||
segatools: [] as Package[],
|
||||
unsupported: [] as Package[] | undefined,
|
||||
};
|
||||
grouped.unsupported = [];
|
||||
for (const pkg of searchedPkgs) {
|
||||
const loc = pkg.loc;
|
||||
if (!loc || !loc.status || typeof loc.status === 'string') {
|
||||
grouped.unsupported.push(pkg);
|
||||
} else {
|
||||
if (
|
||||
loc.status.OK[0] &
|
||||
(Feature.GameDLL | Feature.Mempatcher | Feature.AmdDLL)
|
||||
) {
|
||||
grouped.native.push(pkg);
|
||||
} else if (loc.status.OK[0] & Feature.Mod) {
|
||||
grouped.standard.push(pkg);
|
||||
}
|
||||
if (
|
||||
loc.status.OK[0] &
|
||||
(Feature.AMNet |
|
||||
Feature.Aime |
|
||||
Feature.ChuniIO |
|
||||
Feature.ChusanHook |
|
||||
Feature.Mu3IO |
|
||||
Feature.Mu3Hook)
|
||||
) {
|
||||
grouped.segatools.push(pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (grouped.unsupported.length === 0) {
|
||||
delete grouped.unsupported;
|
||||
}
|
||||
} else {
|
||||
grouped = {} as { [key: string]: Package[] };
|
||||
for (const pkg of searchedPkgs) {
|
||||
for (const cat of pkg.rmt?.categories ?? []) {
|
||||
if (client.hiddenCategories.includes(cat)) {
|
||||
continue;
|
||||
}
|
||||
if (!(cat in grouped)) {
|
||||
grouped[cat] = [] as Package[];
|
||||
}
|
||||
grouped[cat].push(pkg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let res: [string, Package[]][] = [];
|
||||
for (const [k, v] of Object.entries(grouped)) {
|
||||
if (v !== undefined) {
|
||||
res.push([k, v]);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
client.pkgListMode === 'namespace' ||
|
||||
client.pkgListMode === 'category'
|
||||
) {
|
||||
res.sort((a, b) => `${a[0]}`.localeCompare(`${b[0]}`));
|
||||
} else if (client.pkgListMode === 'type') {
|
||||
for (const entry of res) {
|
||||
entry[0] = t(`pkglist.${entry[0]}`);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
});
|
||||
|
||||
const missing = computed(() => {
|
||||
return prf.current?.data.mods.filter((m) => !pkgs.hasLocal(m)) ?? [];
|
||||
});
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
|
||||
const defaultModel = {
|
||||
name: '',
|
||||
description: '',
|
||||
website: '',
|
||||
type: 'rainy',
|
||||
games: [] as string[],
|
||||
};
|
||||
|
||||
const creatorModel = ref({ ...defaultModel });
|
||||
|
||||
const gameModel = (game: Game) =>
|
||||
computed({
|
||||
get() {
|
||||
return (creatorModel.value.games as string[]).includes(game);
|
||||
},
|
||||
set(v: boolean) {
|
||||
creatorModel.value.games = creatorModel.value.games.filter(
|
||||
(g) => g !== game
|
||||
);
|
||||
if (v) {
|
||||
creatorModel.value.games.push(game);
|
||||
}
|
||||
},
|
||||
});
|
||||
const gameModelOngeki = gameModel('ongeki');
|
||||
const gameModelChunithm = gameModel('chunithm');
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Fieldset legend="Missing" v-if="(missing?.length ?? 0) > 0">
|
||||
<Dialog
|
||||
modal
|
||||
:visible="dialogVisible"
|
||||
:closable="false"
|
||||
:header="t('creator.header')"
|
||||
:style="{ width: '500px', scale: client.scaleValue }"
|
||||
class="creation-dialog"
|
||||
>
|
||||
<div style="position: absolute; left: 250px; top: 25px">
|
||||
<a
|
||||
href="https://gitea.tendokyu.moe/akanyan/STARTLINER/wiki/Package-format"
|
||||
target="_blank"
|
||||
style="text-decoration: underline"
|
||||
class="self-center"
|
||||
>{{ t('creator.packageFormat') }}</a
|
||||
>
|
||||
</div>
|
||||
<h2>{{ t('creator.basic') }}</h2>
|
||||
<div class="flex flex-col gap-3">
|
||||
<InputText
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
:placeholder="t('creator.name')"
|
||||
v-model="creatorModel.name"
|
||||
/>
|
||||
<InputText
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
:placeholder="t('creator.description')"
|
||||
v-model="creatorModel.description"
|
||||
/>
|
||||
<InputText
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
:placeholder="t('creator.website')"
|
||||
v-model="creatorModel.website"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h2>{{ t('creator.type') }}</h2>
|
||||
<div class="flex flex-col items-center">
|
||||
<SelectButton
|
||||
:options="[
|
||||
{ title: t('creator.rainy'), value: 'rainy' },
|
||||
{ title: t('creator.native'), value: 'native' },
|
||||
{ title: t('creator.segatools'), value: 'segatools' },
|
||||
]"
|
||||
v-model="creatorModel.type"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h2>{{ t('creator.games') }}</h2>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-row">
|
||||
<div class="grow">{{ t('game.ongeki') }}</div>
|
||||
<ToggleSwitch v-model="gameModelOngeki" />
|
||||
</div>
|
||||
<div class="flex flex-row">
|
||||
<div class="grow">{{ t('game.chunithm') }}</div>
|
||||
<ToggleSwitch v-model="gameModelChunithm" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row mt-5">
|
||||
<Button
|
||||
class="ml-auto mr-1"
|
||||
style="width: 80px"
|
||||
:label="t('ok')"
|
||||
:disabled="creatorModel.games.length === 0"
|
||||
@click="
|
||||
async () => {
|
||||
await invoke('create_package', creatorModel);
|
||||
await pkgs.reloadAll();
|
||||
loadPackages();
|
||||
dialogVisible = false;
|
||||
creatorModel = { ...defaultModel };
|
||||
}
|
||||
"
|
||||
/>
|
||||
<Button
|
||||
class="mr-auto ml-1"
|
||||
style="width: 80px"
|
||||
:label="t('cancel')"
|
||||
@click="
|
||||
() => (
|
||||
(dialogVisible = false),
|
||||
(creatorModel = { ...defaultModel })
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</Dialog>
|
||||
<div class="flex flex-row">
|
||||
<SelectButton
|
||||
:options="[
|
||||
{ title: t('pkglist.namespace'), value: 'namespace' },
|
||||
{ title: t('pkglist.type'), value: 'type' },
|
||||
{ title: t('pkglist.category'), value: 'category' },
|
||||
]"
|
||||
v-model="client.pkgListMode"
|
||||
v-on:update:model-value="
|
||||
client.save();
|
||||
emit('reload-icons');
|
||||
"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="grow text-right mr-2 self-center"
|
||||
v-if="client.pkgListMode === 'category'"
|
||||
>
|
||||
{{ t('pkglist.exclusions') }}
|
||||
</div>
|
||||
<MultiSelect
|
||||
v-if="client.pkgListMode === 'category'"
|
||||
style="width: 30%"
|
||||
:showToggleAll="false"
|
||||
v-model="client.hiddenCategories"
|
||||
v-on:value-change="
|
||||
client.save();
|
||||
emit('reload-icons');
|
||||
"
|
||||
:options="allCategories"
|
||||
class="w-full grow"
|
||||
/>
|
||||
</div>
|
||||
<Fieldset :legend="t('pkglist.missing')" v-if="(missing?.length ?? 0) > 0">
|
||||
<div class="flex items-center" v-for="p in missing">
|
||||
<ModTitlecard
|
||||
show-namespace
|
||||
@ -78,8 +329,28 @@ const missing = computed(() => {
|
||||
/>
|
||||
</div>
|
||||
</Fieldset>
|
||||
<Fieldset v-for="(namespace, key) in group" :legend="key.toString()">
|
||||
<ModListEntry v-for="p in namespace" :pkg="p" />
|
||||
<Fieldset :legend="t('pkglist.local')">
|
||||
<ModListEntry v-for="p in local" :pkg="p" />
|
||||
<Button
|
||||
rounded
|
||||
icon="pi pi-plus"
|
||||
severity="success"
|
||||
aria-label="install"
|
||||
size="small"
|
||||
class="self-center"
|
||||
style="width: 2rem; height: 2rem"
|
||||
v-on:click="() => (dialogVisible = true)"
|
||||
/>
|
||||
</Fieldset>
|
||||
<Fieldset v-for="[namespace, pkgs] in groupedList" :legend="namespace">
|
||||
<ModListEntry v-for="p in pkgs" :pkg="p" />
|
||||
</Fieldset>
|
||||
<div v-if="empty === true" class="text-3xl fadein">∅</div>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.creation-dialog h2 {
|
||||
margin-top: 0.6em;
|
||||
margin-bottom: 0.4em;
|
||||
font-size: 110%;
|
||||
}
|
||||
</style>
|
||||
|
@ -7,12 +7,16 @@ import LinkButton from './LinkButton.vue';
|
||||
import ModTitlecard from './ModTitlecard.vue';
|
||||
import UpdateButton from './UpdateButton.vue';
|
||||
import { invoke } from '../invoke';
|
||||
import { usePkgStore, usePrfStore } from '../stores';
|
||||
import { useClientStore, usePkgStore, usePrfStore } from '../stores';
|
||||
import { Feature, Package } from '../types';
|
||||
import { hasFeature } from '../util';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const prf = usePrfStore();
|
||||
const pkgs = usePkgStore();
|
||||
const client = useClientStore();
|
||||
|
||||
const props = defineProps({
|
||||
pkg: Object as () => Package,
|
||||
@ -36,14 +40,15 @@ if (unsupported.value === true && model.value === true) {
|
||||
|
||||
<template>
|
||||
<div class="flex items-center">
|
||||
<ModTitlecard show-version show-icon show-description :pkg="pkg" />
|
||||
<ModTitlecard
|
||||
show-version
|
||||
show-icon
|
||||
show-description
|
||||
:show-namespace="client.pkgListMode !== 'namespace'"
|
||||
:pkg="pkg"
|
||||
/>
|
||||
<UpdateButton :pkg="pkg" />
|
||||
<span
|
||||
v-tooltip="
|
||||
unsupported &&
|
||||
'This package is currently incompatible with STARTLINER.'
|
||||
"
|
||||
>
|
||||
<span v-tooltip="unsupported && t('store.incompatible')">
|
||||
<ToggleSwitch
|
||||
v-if="hasFeature(pkg, Feature.Mod) || unsupported === true"
|
||||
class="scale-[1.33] shrink-0"
|
||||
|
@ -8,6 +8,9 @@ import ModStoreEntry from './ModStoreEntry.vue';
|
||||
import { invoke } from '../invoke';
|
||||
import { usePkgStore, usePrfStore } from '../stores';
|
||||
import { pkgKey } from '../util';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const pkgs = usePkgStore();
|
||||
const prf = usePrfStore();
|
||||
@ -20,7 +23,7 @@ const props = defineProps({
|
||||
const gameSublist: Ref<string[]> = ref([]);
|
||||
|
||||
invoke('get_game_packages', {
|
||||
game: prf.current?.meta.game,
|
||||
game: prf.current?.meta.game ?? null,
|
||||
}).then((list) => {
|
||||
gameSublist.value = list as string[];
|
||||
});
|
||||
@ -43,10 +46,10 @@ const list = () => {
|
||||
};
|
||||
|
||||
const shouldShowRecommended = computed(() => {
|
||||
if (prf.current!.meta.game === 'ongeki') {
|
||||
if (prf.current?.meta.game === 'ongeki') {
|
||||
return !pkgs.allLocal.some((p) => pkgKey(p) === 'segatools-mu3hook');
|
||||
}
|
||||
if (prf.current!.meta.game === 'chunithm') {
|
||||
if (prf.current?.meta.game === 'chunithm') {
|
||||
return (
|
||||
!pkgs.allLocal.some((p) => pkgKey(p) === 'segatools-chusanhook') ||
|
||||
!pkgs.allLocal.some((p) => pkgKey(p) === 'mempatcher-mempatcher')
|
||||
@ -55,21 +58,21 @@ const shouldShowRecommended = computed(() => {
|
||||
return false;
|
||||
});
|
||||
|
||||
const getRecommendedTooltip = () => {
|
||||
if (prf.current!.meta.game === 'ongeki') {
|
||||
const recommendedTooltip = computed(() => {
|
||||
if (prf.current?.meta.game === 'ongeki') {
|
||||
return 'segatools-mu3hook';
|
||||
}
|
||||
if (prf.current!.meta.game === 'chunithm') {
|
||||
return 'segatools-chusanhook and mempatcher';
|
||||
if (prf.current?.meta.game === 'chunithm') {
|
||||
return 'segatools-chusanhook + mempatcher';
|
||||
}
|
||||
return '';
|
||||
};
|
||||
});
|
||||
|
||||
const installRecommended = () => {
|
||||
if (prf.current!.meta.game === 'ongeki') {
|
||||
if (prf.current?.meta.game === 'ongeki') {
|
||||
pkgs.installFromKey('segatools-mu3hook');
|
||||
}
|
||||
if (prf.current!.meta.game === 'chunithm') {
|
||||
if (prf.current?.meta.game === 'chunithm') {
|
||||
pkgs.installFromKey('segatools-chusanhook');
|
||||
pkgs.installFromKey('mempatcher-mempatcher');
|
||||
}
|
||||
@ -80,15 +83,17 @@ const installRecommended = () => {
|
||||
<div class="flex gap-4 items-center">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex gap-2">
|
||||
<div class="grow">Show installed</div>
|
||||
<div class="grow">{{ t('store.installed') }}</div>
|
||||
<ToggleSwitch v-model="pkgs.showInstalled" />
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<div class="text-amber-400 grow">Show deprecated</div>
|
||||
<div class="text-amber-400 grow">
|
||||
{{ t('store.deprecated') }}
|
||||
</div>
|
||||
<ToggleSwitch v-model="pkgs.showDeprecated" />
|
||||
</div>
|
||||
<!-- <div class="flex gap-2">
|
||||
<div class="text-red-400 grow">Show NSFW</div>
|
||||
<div class="text-red-400 grow">{{ t('store.nsfw') }}</div>
|
||||
<ToggleSwitch v-model="pkgs.showNSFW" />
|
||||
</div> -->
|
||||
</div>
|
||||
@ -96,7 +101,7 @@ const installRecommended = () => {
|
||||
<MultiSelect
|
||||
size="small"
|
||||
:showToggleAll="false"
|
||||
placeholder="Include categories"
|
||||
:placeholder="t('store.includeCategories')"
|
||||
v-model="pkgs.includeCategories"
|
||||
:options="[...pkgs.availableCategories]"
|
||||
class="w-full"
|
||||
@ -104,7 +109,7 @@ const installRecommended = () => {
|
||||
<MultiSelect
|
||||
size="small"
|
||||
:showToggleAll="false"
|
||||
placeholder="Exclude categories"
|
||||
:placeholder="t('store.excludeCategories')"
|
||||
v-model="pkgs.excludeCategories"
|
||||
:options="[...pkgs.availableCategories]"
|
||||
class="w-full"
|
||||
@ -114,8 +119,8 @@ const installRecommended = () => {
|
||||
<Divider />
|
||||
<Button
|
||||
v-if="shouldShowRecommended"
|
||||
label="Install recommended packages"
|
||||
v-tooltip="getRecommendedTooltip"
|
||||
:label="t('store.installRecommended')"
|
||||
v-tooltip="recommendedTooltip"
|
||||
icon="pi pi-plus"
|
||||
class="mb-3"
|
||||
@click="installRecommended"
|
||||
|
@ -1,9 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import Chip from 'primevue/chip';
|
||||
import { convertFileSrc } from '@tauri-apps/api/core';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { invoke } from '../invoke';
|
||||
import { Feature, Package } from '../types';
|
||||
import { hasFeature, needsUpdate } from '../util';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps({
|
||||
pkg: Object as () => Package,
|
||||
@ -14,23 +19,34 @@ const props = defineProps({
|
||||
showIcon: Boolean,
|
||||
});
|
||||
|
||||
const iconSrc = computed(() => {
|
||||
const icon = props.pkg?.loc?.icon ?? props.pkg?.rmt?.icon;
|
||||
const icon = ref('/no-icon.png');
|
||||
|
||||
if (icon === undefined) {
|
||||
return '';
|
||||
} else if (icon.startsWith('https://')) {
|
||||
return icon;
|
||||
const reloadIcons = async () => {
|
||||
const src = props.pkg?.loc?.icon ?? props.pkg?.rmt?.icon;
|
||||
|
||||
if (src === undefined) {
|
||||
icon.value = '/no-icon.png';
|
||||
} else if (src.startsWith('https://')) {
|
||||
icon.value = src;
|
||||
} else {
|
||||
return convertFileSrc(icon);
|
||||
const convt = convertFileSrc(src);
|
||||
if (await invoke('file_exists', { path: src })) {
|
||||
icon.value = convt;
|
||||
} else {
|
||||
icon.value = '/no-icon.png';
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
reloadIcons();
|
||||
|
||||
listen('reload-icons', reloadIcons);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<img
|
||||
v-if="showIcon"
|
||||
:src="iconSrc"
|
||||
:src="icon"
|
||||
class="self-center rounded-sm"
|
||||
width="32px"
|
||||
height="32px"
|
||||
@ -89,7 +105,12 @@ const iconSrc = computed(() => {
|
||||
v-if="showNamespace && pkg?.namespace"
|
||||
class="text-sm opacity-75"
|
||||
>
|
||||
by {{ pkg.namespace }}
|
||||
{{
|
||||
t('by', { namespace: pkg.namespace }).replaceAll(
|
||||
' ',
|
||||
' '
|
||||
)
|
||||
}}
|
||||
</span>
|
||||
<span class="m-2">
|
||||
<span
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ComputedRef, computed, onMounted } from 'vue';
|
||||
import { ComputedRef, computed, ref } from 'vue';
|
||||
import Button from 'primevue/button';
|
||||
import Carousel from 'primevue/carousel';
|
||||
import Dialog from 'primevue/dialog';
|
||||
@ -7,11 +7,14 @@ import { fromKeycode } from '../keyboard';
|
||||
import { useClientStore, usePrfStore } from '../stores';
|
||||
import { prettyPrint } from '../util';
|
||||
import { VueMarkdownIt } from '@f3ve/vue-markdown-it';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const prf = usePrfStore();
|
||||
const client = useClientStore();
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
visible: Boolean,
|
||||
firstTime: Boolean,
|
||||
onFinish: Function,
|
||||
@ -24,38 +27,73 @@ interface Datum {
|
||||
|
||||
const game = computed(() => prf.current?.meta.game);
|
||||
|
||||
const processText = (s: string) => {
|
||||
const processText = computed(() => (s: string) => {
|
||||
// Why do I have to do this
|
||||
s = s
|
||||
.split('\n')
|
||||
.map((l) => l.trim())
|
||||
.join('\n');
|
||||
if (prf.current!.data.keyboard?.data.enabled) {
|
||||
const testKey = prf.current!.data.keyboard?.data.test;
|
||||
const readable = fromKeycode(testKey);
|
||||
if (readable !== null) {
|
||||
return s.replace(
|
||||
'%TESTMENU%',
|
||||
`${readable} or a button on the back of the controller`
|
||||
`${readable} ${t('onboarding.or')} ${t('onboarding.backButton')}`
|
||||
);
|
||||
}
|
||||
}
|
||||
return s.replace('%TESTMENU%', 'a button on the back of the controller');
|
||||
};
|
||||
return s.replace('%TESTMENU%', t('onboarding.backButton'));
|
||||
});
|
||||
|
||||
const loadPage = async (title: string) => {
|
||||
const loadPage = computed(() => (title: string, messages?: object) => {
|
||||
return {
|
||||
text: await (await fetch(`/help-${title}.md`)).text(),
|
||||
text: t(`onboarding.${title}`, {
|
||||
endlink: '</a>',
|
||||
black: '<span class="bg-black text-white">',
|
||||
end: '</span>',
|
||||
...messages,
|
||||
}),
|
||||
image: `help-${title}.png`,
|
||||
};
|
||||
};
|
||||
|
||||
let systemProcessing: Datum;
|
||||
let standardOngeki: Datum;
|
||||
let standardChunithm: Datum;
|
||||
let lever: Datum;
|
||||
let server: Datum;
|
||||
let finaleOngeki: Datum;
|
||||
let finaleChunithm: Datum;
|
||||
});
|
||||
|
||||
const data: ComputedRef<Datum[]> = computed(() => {
|
||||
const res = [];
|
||||
|
||||
const [standard, systemProcessing, lever, server, finale] = [
|
||||
loadPage.value('standard', {
|
||||
bigblack: '<div class="p-2 mt-1 mb-1 bg-black text-white">',
|
||||
endbig: '</div>',
|
||||
}),
|
||||
loadPage.value('ongeki-system-processing'),
|
||||
loadPage.value('ongeki-lever'),
|
||||
loadPage.value('chunithm-server', {
|
||||
link: '<a href="https://gitea.tendokyu.moe/Dniel97/SEGAguide/wiki/FAQ#game-is-stuck-at-checking-distribution-server" target="_blank">',
|
||||
}),
|
||||
loadPage.value('finale', {
|
||||
segaguide:
|
||||
'<a href="https://gitea.tendokyu.moe/Dniel97/SEGAguide/wiki/FAQ" target="_blank">',
|
||||
twotorial: '<a href="https://two-torial.xyz/" target="_blank">',
|
||||
}),
|
||||
];
|
||||
const standardOngeki = {
|
||||
...standard,
|
||||
image: '/help-standard-ongeki.png',
|
||||
};
|
||||
const standardChunithm = {
|
||||
...standard,
|
||||
image: '/help-standard-chunithm.png',
|
||||
};
|
||||
const finaleOngeki = {
|
||||
...finale,
|
||||
image: '/help-finale-ongeki.png',
|
||||
};
|
||||
const finaleChunithm = {
|
||||
...finale,
|
||||
image: '/help-finale-chunithm.png',
|
||||
};
|
||||
|
||||
switch (prf.current?.meta.game) {
|
||||
case 'ongeki':
|
||||
res.push(systemProcessing);
|
||||
@ -75,32 +113,18 @@ const data: ComputedRef<Datum[]> = computed(() => {
|
||||
return res;
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
[standardOngeki, systemProcessing, lever, server, finaleOngeki] =
|
||||
await Promise.all([
|
||||
loadPage('standard'),
|
||||
loadPage('ongeki-system-processing'),
|
||||
loadPage('ongeki-lever'),
|
||||
loadPage('chunithm-server'),
|
||||
loadPage('finale'),
|
||||
]);
|
||||
standardOngeki = {
|
||||
...standardOngeki,
|
||||
image: '/help-standard-ongeki.png',
|
||||
};
|
||||
standardChunithm = {
|
||||
...standardOngeki,
|
||||
image: '/help-standard-chunithm.png',
|
||||
};
|
||||
finaleOngeki = {
|
||||
...finaleOngeki,
|
||||
image: '/help-finale-ongeki.png',
|
||||
};
|
||||
finaleChunithm = {
|
||||
...finaleOngeki,
|
||||
image: '/help-finale-chunithm.png',
|
||||
};
|
||||
const context = ref({
|
||||
index: 0,
|
||||
});
|
||||
|
||||
const exitLabel = computed(() => {
|
||||
return props.firstTime === true &&
|
||||
context.value.index < data.value.length - 1
|
||||
? t('skip')
|
||||
: t('close');
|
||||
});
|
||||
|
||||
const page = ref(0);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -114,8 +138,16 @@ onMounted(async () => {
|
||||
: `${game ? prettyPrint(game) : '<game>'} help`
|
||||
"
|
||||
:style="{ width: '760px', scale: client.scaleValue }"
|
||||
v-on:show="() => ((context.index = 0), (page = 0))"
|
||||
>
|
||||
<Carousel :value="data" :num-visible="1" :num-scroll="1">
|
||||
<Carousel
|
||||
:value="data"
|
||||
:num-visible="1"
|
||||
:num-scroll="1"
|
||||
:context="context"
|
||||
:page="page"
|
||||
v-on:update:page="(p) => ((context.index = p), (page = p))"
|
||||
>
|
||||
<template #item="slotProps">
|
||||
<div class="md-container markdown">
|
||||
<vue-markdown-it
|
||||
@ -135,9 +167,15 @@ onMounted(async () => {
|
||||
</template>
|
||||
</Carousel>
|
||||
<div style="width: 100%; text-align: center">
|
||||
<Button
|
||||
v-if="context.index < data.length - 1"
|
||||
class="m-auto mr-4"
|
||||
:label="t('next')"
|
||||
@click="() => (page += 1)"
|
||||
/>
|
||||
<Button
|
||||
class="m-auto"
|
||||
label="OK"
|
||||
:label="exitLabel"
|
||||
@click="() => onFinish && onFinish()"
|
||||
/>
|
||||
</div>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import InputNumber from 'primevue/inputnumber';
|
||||
import SelectButton from 'primevue/selectbutton';
|
||||
import ToggleSwitch from 'primevue/toggleswitch';
|
||||
import FileEditor from './FileEditor.vue';
|
||||
@ -13,56 +14,41 @@ import NetworkOptions from './options/Network.vue';
|
||||
import SegatoolsOptions from './options/Segatools.vue';
|
||||
import StartlinerOptions from './options/Startliner.vue';
|
||||
import { usePrfStore } from '../stores';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const prf = usePrfStore();
|
||||
|
||||
const audioModel = computed({
|
||||
const blacklistMinModel = computed({
|
||||
get() {
|
||||
return prf.current?.data.mu3_ini?.audio ?? null;
|
||||
},
|
||||
set(value: 'Shared' | 'Excl6Ch' | 'Excl2Ch') {
|
||||
if (prf.current!.data.mu3_ini === undefined) {
|
||||
prf.current!.data.mu3_ini = {};
|
||||
if (prf.current?.data.mu3_ini?.blacklist === undefined) {
|
||||
return null;
|
||||
}
|
||||
prf.current!.data.mu3_ini!.audio = value;
|
||||
return prf.current?.data.mu3_ini?.blacklist[0];
|
||||
},
|
||||
set(value: number) {
|
||||
prf.current!.data.mu3_ini!.blacklist = [
|
||||
value,
|
||||
prf.current!.data.mu3_ini!.blacklist?.[1] ?? 19999,
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
// const blacklistMinModel = computed({
|
||||
// get() {
|
||||
// if (prf.current?.data.mu3_ini?.blacklist === undefined) {
|
||||
// return null;
|
||||
// }
|
||||
// return prf.current?.data.mu3_ini?.blacklist[0];
|
||||
// },
|
||||
// set(value: number) {
|
||||
// if (prf.current!.data.mu3_ini === undefined) {
|
||||
// prf.current!.data.mu3_ini = {};
|
||||
// }
|
||||
// prf.current!.data.mu3_ini!.blacklist = [
|
||||
// value,
|
||||
// prf.current!.data.mu3_ini!.blacklist?.[1] ?? 19999,
|
||||
// ];
|
||||
// },
|
||||
// });
|
||||
|
||||
// const blacklistMaxModel = computed({
|
||||
// get() {
|
||||
// if (prf.current?.data.mu3_ini?.blacklist === undefined) {
|
||||
// return null;
|
||||
// }
|
||||
// return prf.current?.data.mu3_ini?.blacklist[1];
|
||||
// },
|
||||
// set(value: number) {
|
||||
// if (prf.current!.data.mu3_ini === undefined) {
|
||||
// prf.current!.data.mu3_ini = {};
|
||||
// }
|
||||
// prf.current!.data.mu3_ini!.blacklist = [
|
||||
// prf.current!.data.mu3_ini!.blacklist?.[0] ?? 10000,
|
||||
// value,
|
||||
// ];
|
||||
// },
|
||||
// });
|
||||
const blacklistMaxModel = computed({
|
||||
get() {
|
||||
if (prf.current?.data.mu3_ini?.blacklist === undefined) {
|
||||
return null;
|
||||
}
|
||||
return prf.current?.data.mu3_ini.blacklist[1];
|
||||
},
|
||||
set(value: number) {
|
||||
prf.current!.data.mu3_ini!.blacklist = [
|
||||
prf.current!.data.mu3_ini!.blacklist?.[0] ?? 10000,
|
||||
value,
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
prf.reload();
|
||||
</script>
|
||||
@ -75,9 +61,9 @@ prf.reload();
|
||||
<MiscOptions />
|
||||
<OptionCategory
|
||||
title="Extensions"
|
||||
v-if="prf.current!.meta.game === 'chunithm'"
|
||||
v-if="prf.current?.meta.game === 'chunithm'"
|
||||
>
|
||||
<OptionRow title="Saekawa config">
|
||||
<OptionRow :title="t('cfg.extensions.saekawa')">
|
||||
<FileEditor
|
||||
filename="saekawa.toml"
|
||||
promptname="saekawa config file"
|
||||
@ -85,51 +71,76 @@ prf.reload();
|
||||
/> </OptionRow
|
||||
></OptionCategory>
|
||||
<OptionCategory
|
||||
title="Extensions"
|
||||
v-if="prf.current!.meta.game === 'ongeki'"
|
||||
:title="t('cfg.extensions.title')"
|
||||
v-if="prf.current?.meta.game === 'ongeki'"
|
||||
>
|
||||
<OptionRow title="Inohara config">
|
||||
<OptionRow :title="t('cfg.extensions.inohara')">
|
||||
<FileEditor
|
||||
filename="inohara.cfg"
|
||||
promptname="inohara config file"
|
||||
extension="cfg"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow title="BepInEx console">
|
||||
<OptionRow :title="t('cfg.extensions.bepInExConsole')">
|
||||
<!-- @vue-expect-error -->
|
||||
<ToggleSwitch v-model="prf.current!.data.bepinex.console" />
|
||||
</OptionRow>
|
||||
|
||||
<OptionRow
|
||||
title="Audio mode"
|
||||
tooltip="Exclusive 2-channel mode requires a patch"
|
||||
:title="t('cfg.extensions.audioMode')"
|
||||
:tooltip="t('cfg.extensions.audioTooltip')"
|
||||
>
|
||||
<SelectButton
|
||||
v-model="audioModel"
|
||||
v-model="prf.current!.data.mu3_ini!.audio"
|
||||
:options="[
|
||||
{ title: 'Shared', value: 'Shared' },
|
||||
{ title: 'Exclusive 6-channel', value: 'Excl6Ch' },
|
||||
{ title: 'Exclusive 2-channel', value: 'Excl2Ch' },
|
||||
{ title: t('cfg.extensions.audioShared'), value: 'Shared' },
|
||||
{ title: t('cfg.extensions.audio6Ch'), value: 'Excl6Ch' },
|
||||
{ title: t('cfg.extensions.audio2Ch'), value: 'Excl2Ch' },
|
||||
]"
|
||||
:allow-empty="true"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
/></OptionRow>
|
||||
|
||||
<!-- <OptionRow
|
||||
<OptionRow
|
||||
:title="t('cfg.extensions.sampleRate')"
|
||||
v-if="
|
||||
prf.current?.data.mods.includes(
|
||||
'7EVENDAYSHOLIDAYS-ExclusiveAudio'
|
||||
)
|
||||
"
|
||||
>
|
||||
<SelectButton
|
||||
v-model="prf.current!.data.mu3_ini!.sample_rate"
|
||||
:disabled="prf.current!.data.mu3_ini!.audio === 'Shared'"
|
||||
:options="[
|
||||
{ title: '44.1KHz', value: 44100 },
|
||||
{ title: '48KHz', value: 48000 },
|
||||
{ title: '96KHz', value: 96000 },
|
||||
{ title: '192KHz', value: 192000 },
|
||||
]"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
/></OptionRow>
|
||||
|
||||
<OptionRow
|
||||
v-if="
|
||||
prf.current?.data.mods.includes('7EVENDAYSHOLIDAYS-Blacklist')
|
||||
"
|
||||
class="number-input"
|
||||
title="Song ID Blacklist"
|
||||
tooltip="Requires a patch"
|
||||
:title="t('cfg.extensions.blacklist')"
|
||||
:tooltip="t('cfg.extensions.blacklistTooltip')"
|
||||
><InputNumber
|
||||
class="shrink"
|
||||
size="small"
|
||||
:min="10000"
|
||||
:min="9000"
|
||||
:max="99999"
|
||||
placeholder="10000"
|
||||
:use-grouping="false"
|
||||
:allow-empty="false"
|
||||
v-model="blacklistMinModel" />
|
||||
x
|
||||
~
|
||||
<InputNumber
|
||||
class="shrink"
|
||||
size="small"
|
||||
@ -139,7 +150,36 @@ prf.reload();
|
||||
:use-grouping="false"
|
||||
:allow-empty="false"
|
||||
v-model="blacklistMaxModel"
|
||||
/></OptionRow> -->
|
||||
/></OptionRow>
|
||||
<OptionRow
|
||||
class="number-input"
|
||||
title="GP"
|
||||
v-if="
|
||||
prf.current?.data.mods.includes('7EVENDAYSHOLIDAYS-DisableGP')
|
||||
"
|
||||
><InputNumber
|
||||
class="shrink"
|
||||
size="small"
|
||||
:min="0"
|
||||
:max="9999"
|
||||
:use-grouping="false"
|
||||
:allow-empty="false"
|
||||
v-model="prf.current!.data.mu3_ini!.gp"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
:title="t('cfg.extensions.bonusTracks')"
|
||||
:tooltip="t('cfg.extensions.bonusTracksTooltip')"
|
||||
v-if="
|
||||
prf.current?.data.mods.includes(
|
||||
'7EVENDAYSHOLIDAYS-UnlockAllMusic'
|
||||
)
|
||||
"
|
||||
>
|
||||
<ToggleSwitch
|
||||
v-model="prf.current!.data.mu3_ini!.enable_bonus_tracks"
|
||||
/>
|
||||
</OptionRow>
|
||||
</OptionCategory>
|
||||
<KeyboardOptions />
|
||||
<StartlinerOptions />
|
||||
|
@ -1,9 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import InputNumber from 'primevue/inputnumber';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import ToggleSwitch from 'primevue/toggleswitch';
|
||||
import OptionRow from './OptionRow.vue';
|
||||
import { usePrfStore } from '../stores';
|
||||
import { Patch } from '../types';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t, te } = useI18n();
|
||||
|
||||
const prf = usePrfStore();
|
||||
|
||||
@ -23,20 +28,40 @@ const setNumber = (key: string, val: number) => {
|
||||
}
|
||||
};
|
||||
|
||||
defineProps({
|
||||
const props = defineProps({
|
||||
patch: Object as () => Patch,
|
||||
});
|
||||
|
||||
// One day, I will repent
|
||||
const hexModel = computed({
|
||||
get() {
|
||||
const hex = (prf.current!.data.patches[props.patch!.id!] as any)?.hex;
|
||||
if (hex !== undefined) {
|
||||
return new TextDecoder().decode(new Int8Array(hex).buffer);
|
||||
} else {
|
||||
return 'FREE PLAY';
|
||||
}
|
||||
},
|
||||
set(value: string) {
|
||||
(prf.current!.data.patches[props.patch!.id!] as any) = {
|
||||
hex: new TextEncoder().encode(value),
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
// Doesn't need to be reactive
|
||||
const nameKey = `patch.${props.patch?.id}`;
|
||||
const name = te(nameKey) ? t(nameKey) : props.patch?.name;
|
||||
|
||||
const tooltipKey = `patch.${props.patch?.id}-tooltip`;
|
||||
const tooltip = te(tooltipKey) ? t(tooltipKey) : props.patch?.tooltip;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<OptionRow
|
||||
:title="patch?.name"
|
||||
:tooltip="patch?.tooltip"
|
||||
:greytext="patch?.id"
|
||||
>
|
||||
<OptionRow :title="name" :tooltip="tooltip" :greytext="patch?.id">
|
||||
<ToggleSwitch
|
||||
v-if="patch?.type === undefined"
|
||||
:model-value="prf.current!.data.patches[patch!.id!] !== undefined"
|
||||
:model-value="prf.current!.data.patches?.[patch!.id!] !== undefined"
|
||||
@update:model-value="(v: boolean) => toggleUnary(patch!.id!, v)"
|
||||
/>
|
||||
<InputNumber
|
||||
@ -50,5 +75,6 @@ defineProps({
|
||||
:max="patch?.max"
|
||||
:placeholder="(patch?.default ?? 0).toString()"
|
||||
/>
|
||||
<InputText v-else-if="patch?.type === 'hex'" v-model="hexModel" />
|
||||
</OptionRow>
|
||||
</template>
|
||||
|
@ -1,11 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { Ref, ref } from 'vue';
|
||||
// import Select from 'primevue/select';
|
||||
import * as path from '@tauri-apps/api/path';
|
||||
import OptionCategory from './OptionCategory.vue';
|
||||
import PatchEntry from './PatchEntry.vue';
|
||||
import { invoke } from '../invoke';
|
||||
import { usePrfStore } from '../stores';
|
||||
import { Patch } from '../types';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const prf = usePrfStore();
|
||||
|
||||
@ -29,21 +33,22 @@ invoke('list_patches', { target: prf.current!.data.sgt.target }).then(
|
||||
target: amd,
|
||||
})) as Patch[];
|
||||
})();
|
||||
|
||||
const errorMessage =
|
||||
"No compatible patches found. Make sure you're using unpacked and unpatched files.";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<OptionCategory title="chusanApp.exe" always-found>
|
||||
<OptionCategory
|
||||
v-if="prf.current?.meta.game === 'chunithm'"
|
||||
title="chusanApp.exe"
|
||||
always-found
|
||||
>
|
||||
<PatchEntry
|
||||
v-if="gamePatches !== null"
|
||||
v-for="p in gamePatches"
|
||||
:patch="p"
|
||||
/>
|
||||
<div v-if="gamePatches === null">Loading...</div>
|
||||
<div v-if="gamePatches === null">{{ t('patch.loading') }}</div>
|
||||
<div v-if="gamePatches !== null && gamePatches.length === 0">
|
||||
{{ errorMessage }}
|
||||
{{ t('patch.noneFound') }}
|
||||
</div>
|
||||
</OptionCategory>
|
||||
<OptionCategory title="amdaemon.exe" always-found>
|
||||
@ -52,9 +57,22 @@ const errorMessage =
|
||||
v-for="p in amdPatches"
|
||||
:patch="p"
|
||||
/>
|
||||
<div v-if="gamePatches === null">Loading...</div>
|
||||
<div v-if="gamePatches === null">{{ t('patch.loading') }}</div>
|
||||
<div v-if="amdPatches !== null && amdPatches.length === 0">
|
||||
{{ errorMessage }}
|
||||
{{ t('patch.noneFound') }}
|
||||
<!-- <br />
|
||||
<Select
|
||||
class="mt-3"
|
||||
style="width: 400px"
|
||||
:options="[
|
||||
{},
|
||||
{},
|
||||
]"
|
||||
:placeholder="t('patch.forceLoad')"
|
||||
size="small"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
></Select> -->
|
||||
</div>
|
||||
</OptionCategory>
|
||||
</template>
|
||||
|
@ -1,33 +1,224 @@
|
||||
<script setup lang="ts">
|
||||
import { Ref, ref } from 'vue';
|
||||
import Button from 'primevue/button';
|
||||
import Dialog from 'primevue/dialog';
|
||||
import Select from 'primevue/select';
|
||||
import SelectButton from 'primevue/selectbutton';
|
||||
import ToggleSwitch from 'primevue/toggleswitch';
|
||||
import * as path from '@tauri-apps/api/path';
|
||||
import { open } from '@tauri-apps/plugin-dialog';
|
||||
import ProfileListEntry from './ProfileListEntry.vue';
|
||||
import { usePrfStore } from '../stores';
|
||||
import { invoke } from '../invoke';
|
||||
import { useClientStore, useGeneralStore, usePrfStore } from '../stores';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const prf = usePrfStore();
|
||||
const client = useClientStore();
|
||||
const general = useGeneralStore();
|
||||
|
||||
const hasChunithm = ref(false);
|
||||
const exportVisible = ref(false);
|
||||
const exportKeychip = ref(false);
|
||||
const files = new Set<string>();
|
||||
|
||||
(async () => {
|
||||
hasChunithm.value = (
|
||||
(await invoke('list_platform_capabilities')) as string[]
|
||||
).includes('chunithm');
|
||||
})();
|
||||
|
||||
const exportTemplate = async () => {
|
||||
const fl = [...files.values()];
|
||||
exportVisible.value = false;
|
||||
await invoke('export_profile', {
|
||||
exportKeychip: exportKeychip.value,
|
||||
files: fl,
|
||||
});
|
||||
await invoke('open_file', {
|
||||
path: await path.join(await general.configDir, 'exports'),
|
||||
});
|
||||
};
|
||||
|
||||
const fileList = {
|
||||
ongeki: ['aime.txt', 'inohara.cfg', 'mu3.ini', 'segatools-base.ini'],
|
||||
chunithm: ['aime.txt', 'saekawa.toml', 'segatools-base.ini'],
|
||||
};
|
||||
|
||||
const fileListCurrent: Ref<string[]> = ref([]);
|
||||
|
||||
const recalcFileList = async () => {
|
||||
const res: string[] = [];
|
||||
files.clear();
|
||||
for (const idx in fileList[prf.current!.meta.game]) {
|
||||
const f = fileList[prf.current!.meta.game][idx];
|
||||
const p = await path.join(await prf.configDir, f);
|
||||
if (await invoke('file_exists', { path: p })) {
|
||||
res.push(f);
|
||||
files.add(f);
|
||||
}
|
||||
}
|
||||
fileListCurrent.value = res;
|
||||
};
|
||||
|
||||
const openExportDialog = async () => {
|
||||
await recalcFileList();
|
||||
exportVisible.value = true;
|
||||
};
|
||||
|
||||
const importPick = async () => {
|
||||
const res = await open({
|
||||
multiple: false,
|
||||
directory: false,
|
||||
filters: [
|
||||
{
|
||||
name: t('profile.template'),
|
||||
extensions: ['zip'],
|
||||
},
|
||||
],
|
||||
});
|
||||
if (res != null) {
|
||||
await invoke('import_profile', { path: res });
|
||||
await prf.reloadList();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="prf.list.length === 0">
|
||||
Welcome to STARTLINER! Start by creating a profile.
|
||||
</div>
|
||||
<div class="mt-4 flex flex-row flex-wrap align-middle gap-4">
|
||||
<Button
|
||||
label="O.N.G.E.K.I. profile"
|
||||
icon="pi pi-plus"
|
||||
class="ongeki-button profile-button"
|
||||
@click="() => prf.create('ongeki')"
|
||||
/>
|
||||
<Button
|
||||
label="CHUNITHM profile"
|
||||
icon="pi pi-plus"
|
||||
class="chunithm-button profile-button"
|
||||
@click="() => prf.create('chunithm')"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-12 flex flex-col flex-wrap align-middle gap-4">
|
||||
<div v-for="p in prf.list">
|
||||
<ProfileListEntry :p="p" />
|
||||
<Dialog
|
||||
modal
|
||||
:visible="exportVisible"
|
||||
:closable="false /*this shit doesn't work */"
|
||||
:header="`${t('profile.export')} ${prf.current?.meta.name}`"
|
||||
:style="{ width: '300px', scale: client.scaleValue }"
|
||||
>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-row">
|
||||
<div class="grow">{{ t('profile.export') }} keychip</div>
|
||||
<ToggleSwitch v-model="exportKeychip" />
|
||||
</div>
|
||||
<div class="flex flex-row" v-for="f in fileListCurrent">
|
||||
<div class="grow">{{ t('profile.export') }} {{ f }}</div>
|
||||
<ToggleSwitch
|
||||
:model-value="true"
|
||||
@update:model-value="
|
||||
(v) => {
|
||||
if (v === true) {
|
||||
files.add(f);
|
||||
} else {
|
||||
files.delete(f);
|
||||
}
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div style="width: 100%; text-align: center">
|
||||
<Button
|
||||
class="m-auto mr-3"
|
||||
style="width: 80px"
|
||||
label="OK"
|
||||
@click="() => exportTemplate()"
|
||||
/>
|
||||
<Button
|
||||
class="m-auto"
|
||||
style="width: 80px"
|
||||
label="Cancel"
|
||||
@click="() => (exportVisible = false)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
<div style="float: left">
|
||||
<div v-if="prf.list.length === 0">
|
||||
{{ t('profile.welcome') }}
|
||||
</div>
|
||||
<div class="mt-4 flex flex-row flex-wrap align-middle gap-4">
|
||||
<Button
|
||||
:label="t('profile.create', { game: t('game.ongeki') })"
|
||||
icon="pi pi-file-plus"
|
||||
class="ongeki-button profile-button"
|
||||
@click="() => prf.create('ongeki')"
|
||||
/>
|
||||
<Button
|
||||
v-if="hasChunithm"
|
||||
:label="t('profile.create', { game: t('game.chunithm') })"
|
||||
icon="pi pi-file-plus"
|
||||
class="chunithm-button profile-button"
|
||||
@click="() => prf.create('chunithm')"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-4 flex flex-row flex-wrap align-middle gap-4">
|
||||
<Button
|
||||
:label="t('profile.importTemplate')"
|
||||
icon="pi pi-file-import"
|
||||
class="import-button profile-button"
|
||||
@click="() => importPick()"
|
||||
/>
|
||||
<Button
|
||||
:disabled="prf.current === null"
|
||||
:label="t('profile.exportTemplate')"
|
||||
icon="pi pi-file-export"
|
||||
class="profile-button"
|
||||
@click="() => openExportDialog()"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-12 flex flex-col flex-wrap align-middle gap-4">
|
||||
<div v-for="p in prf.list">
|
||||
<ProfileListEntry :p="p" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="float: right" class="mr-5 mt-3 flex flex-col gap-4 items-end">
|
||||
<div>
|
||||
<div class="pi pi-language mr-2"></div>
|
||||
<Select
|
||||
:model-value="client.locale"
|
||||
@update:model-value="async (v) => await client.setLocale(v)"
|
||||
style="width: 200px"
|
||||
:options="[
|
||||
{ title: 'English', value: 'en' },
|
||||
// { title: '日本語', value: 'ja' },
|
||||
{ title: 'Polski', value: 'pl' },
|
||||
]"
|
||||
size="small"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
></Select>
|
||||
</div>
|
||||
<SelectButton
|
||||
style="height: 50px"
|
||||
v-model="client.scaleModel"
|
||||
:options="[
|
||||
{ title: 'S', size: '0.8em', value: 's' },
|
||||
{ title: 'M', size: '1.0em', value: 'm' },
|
||||
{ title: 'L', size: '1.2em', value: 'l' },
|
||||
{ title: 'XL', size: '1.4em', value: 'xl' },
|
||||
]"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
><template #option="slotProps">
|
||||
<div :style="{ fontSize: slotProps.option.size }">
|
||||
{{ slotProps.option.title }}
|
||||
</div>
|
||||
</template></SelectButton
|
||||
>
|
||||
<SelectButton
|
||||
style="height: 50px"
|
||||
:model-value="client.theme"
|
||||
@update:model-value="(v) => client.setTheme(v)"
|
||||
:options="[
|
||||
{ title: 'System', value: 'system', icon: 'pi pi-home' },
|
||||
{ title: 'Light', value: 'light', icon: 'pi pi-sun' },
|
||||
{ title: 'Dark', value: 'dark', icon: 'pi pi-moon' },
|
||||
]"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
><template #option="slotProps">
|
||||
<div :class="slotProps.option.icon"></div> </template
|
||||
></SelectButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -57,4 +248,14 @@ const prf = usePrfStore();
|
||||
background-color: var(--p-yellow-300) !important;
|
||||
border-color: var(--p-yellow-300) !important;
|
||||
}
|
||||
|
||||
.import-button {
|
||||
background-color: var(--p-purple-400) !important;
|
||||
border-color: var(--p-purple-400) !important;
|
||||
}
|
||||
.import-button:hover,
|
||||
.import-button:active {
|
||||
background-color: var(--p-purple-300) !important;
|
||||
border-color: var(--p-purple-300) !important;
|
||||
}
|
||||
</style>
|
||||
|
@ -7,6 +7,9 @@ import * as path from '@tauri-apps/api/path';
|
||||
import { invoke } from '../invoke';
|
||||
import { useGeneralStore, usePrfStore } from '../stores';
|
||||
import { ProfileMeta } from '../types';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const general = useGeneralStore();
|
||||
const prf = usePrfStore();
|
||||
@ -60,11 +63,23 @@ const deleteProfile = async () => {
|
||||
|
||||
const promptDeleteProfile = async () => {
|
||||
confirmDialog.require({
|
||||
message: `Are you sure you want to delete ${props.p?.game}-${props.p?.name}?`,
|
||||
header: 'Delete profile',
|
||||
message: t('profile.reallyDelete', {
|
||||
profile: `${props.p?.game}-${props.p?.name}`,
|
||||
}),
|
||||
header: t('profile.delete'),
|
||||
accept: deleteProfile,
|
||||
});
|
||||
};
|
||||
|
||||
const dataExists = ref(false);
|
||||
|
||||
general.dataDir.then((dataDir) =>
|
||||
path
|
||||
.join(dataDir, `profile-${props.p!.game}-${props.p!.name}`)
|
||||
.then(async (p) => {
|
||||
dataExists.value = await invoke('file_exists', { path: p });
|
||||
})
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -125,20 +140,43 @@ const promptDeleteProfile = async () => {
|
||||
/>
|
||||
<Button
|
||||
rounded
|
||||
icon="pi pi-folder"
|
||||
icon="pi pi-cog"
|
||||
severity="help"
|
||||
aria-label="open-directory"
|
||||
aria-label="open-config-directory"
|
||||
size="small"
|
||||
class="self-center"
|
||||
style="width: 2rem; height: 2rem"
|
||||
@click="
|
||||
path
|
||||
.join(general.dataDir, `profile-${p!.game}-${p!.name}`)
|
||||
.then(async (path) => {
|
||||
if (await invoke('file_exists', { path })) {
|
||||
async () =>
|
||||
path
|
||||
.join(
|
||||
await general.configDir,
|
||||
`profile-${p!.game}-${p!.name}`
|
||||
)
|
||||
.then(async (path) => {
|
||||
await invoke('open_file', { path });
|
||||
}
|
||||
})
|
||||
})
|
||||
"
|
||||
/>
|
||||
<Button
|
||||
v-if="dataExists"
|
||||
rounded
|
||||
icon="pi pi-folder"
|
||||
severity="help"
|
||||
aria-label="open-data-directory"
|
||||
size="small"
|
||||
class="self-center"
|
||||
style="width: 2rem; height: 2rem"
|
||||
@click="
|
||||
async () =>
|
||||
path
|
||||
.join(
|
||||
await general.dataDir,
|
||||
`profile-${p!.game}-${p!.name}`
|
||||
)
|
||||
.then(async (path) => {
|
||||
await invoke('open_file', { path });
|
||||
})
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
|
@ -8,6 +8,9 @@ import { getCurrentWindow } from '@tauri-apps/api/window';
|
||||
import Onboarding from './Onboarding.vue';
|
||||
import { invoke } from '../invoke';
|
||||
import { useClientStore, usePrfStore } from '../stores';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const prf = usePrfStore();
|
||||
const client = useClientStore();
|
||||
@ -24,22 +27,22 @@ const startline = async (force: boolean, refresh: boolean) => {
|
||||
if (start_check.length > 0) {
|
||||
const message = start_check.map((o) => {
|
||||
if ('MissingRemotePackage' in o) {
|
||||
return `Package missing: ${o.MissingRemotePackage}`;
|
||||
return `${t('start.error.package')}: ${o.MissingRemotePackage}`;
|
||||
} else if ('MissingLocalPackage' in o) {
|
||||
return `Package missing: ${o.MissingLocalPackage}`;
|
||||
return `${t('start.error.package')}: ${o.MissingLocalPackage}`;
|
||||
} else if ('MissingDependency' in o) {
|
||||
return `Dependency missing: ${(o.MissingDependency as string[]).join(' ')}`;
|
||||
return `${t('start.error.dependency')}: ${(o.MissingDependency as string[]).join(' ')}`;
|
||||
} else if ('MissingTool' in o) {
|
||||
return `Tool missing: ${o.MissingTool}`;
|
||||
return `${t('start.error.tool')}: ${o.MissingTool}`;
|
||||
} else {
|
||||
return 'Unknown error';
|
||||
return t('start.error.unknown');
|
||||
}
|
||||
});
|
||||
confirmDialog.require({
|
||||
message: message.join('\n'),
|
||||
header: 'Start check failed',
|
||||
acceptLabel: 'Run anyway',
|
||||
rejectLabel: 'Cancel',
|
||||
header: t('start.failed'),
|
||||
acceptLabel: t('start.accept'),
|
||||
rejectLabel: t('cancel'),
|
||||
accept: () => {
|
||||
startline(true, refresh);
|
||||
},
|
||||
@ -62,16 +65,16 @@ const kill = async () => {
|
||||
|
||||
const disabledTooltip = computed(() => {
|
||||
if (prf.current?.data.sgt.target.length === 0) {
|
||||
return 'The game path must be specified';
|
||||
return t('start.tooltip.game');
|
||||
}
|
||||
if (prf.current?.data.sgt.amfs.length === 0) {
|
||||
return 'The amfs path must be specified';
|
||||
return t('start.tooltip.amfs');
|
||||
}
|
||||
if (
|
||||
prf.current?.data.sgt.hook === null ||
|
||||
prf.current?.data.sgt.hook === undefined
|
||||
) {
|
||||
return 'A segatools hook package is necessary';
|
||||
return t('start.tooltip.segatools');
|
||||
}
|
||||
return null;
|
||||
});
|
||||
@ -96,32 +99,66 @@ const createShortcut = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
label: 'Refresh and start',
|
||||
icon: 'pi pi-sync',
|
||||
tooltip: 'test',
|
||||
command: async () => await startline(false, true),
|
||||
},
|
||||
{
|
||||
label: 'Start unchecked',
|
||||
icon: 'pi pi-exclamation-circle',
|
||||
command: async () => await startline(true, false),
|
||||
},
|
||||
{
|
||||
label: 'Create desktop shortcut',
|
||||
icon: 'pi pi-link',
|
||||
command: createShortcut,
|
||||
},
|
||||
{
|
||||
label: 'Help',
|
||||
icon: 'pi pi-question-circle',
|
||||
command: () => {
|
||||
onboardingFirstTime.value = false;
|
||||
onboardingVisible.value = true;
|
||||
const hasShortcut = ref(false);
|
||||
|
||||
(async () => {
|
||||
hasShortcut.value = (
|
||||
(await invoke('list_platform_capabilities')) as string[]
|
||||
).includes('shortcut');
|
||||
})();
|
||||
|
||||
const menuItems = computed(() => {
|
||||
let base = [
|
||||
{
|
||||
label: t('start.button.unchecked'),
|
||||
icon: 'pi pi-exclamation-circle',
|
||||
command: async () => await startline(true, false),
|
||||
},
|
||||
},
|
||||
];
|
||||
];
|
||||
let baseTail = [
|
||||
{
|
||||
label: t('start.button.help'),
|
||||
icon: 'pi pi-question-circle',
|
||||
command: () => {
|
||||
onboardingFirstTime.value = false;
|
||||
onboardingVisible.value = true;
|
||||
},
|
||||
},
|
||||
];
|
||||
if (prf.current === null) {
|
||||
return [];
|
||||
}
|
||||
if (hasShortcut.value === true) {
|
||||
base = [
|
||||
...base,
|
||||
{
|
||||
label: t('start.button.shortcut'),
|
||||
icon: 'pi pi-link',
|
||||
command: createShortcut,
|
||||
},
|
||||
];
|
||||
}
|
||||
if (prf.current.meta.game === 'chunithm') {
|
||||
return [...base, ...baseTail];
|
||||
}
|
||||
if (prf.current.meta.game === 'ongeki') {
|
||||
return [
|
||||
{
|
||||
label: t('start.button.refresh'),
|
||||
icon: 'pi pi-sync',
|
||||
command: async () => await startline(false, true),
|
||||
},
|
||||
...base,
|
||||
{
|
||||
label: t('start.button.cache'),
|
||||
icon: 'pi pi-trash',
|
||||
command: async () => await invoke('clear_cache'),
|
||||
},
|
||||
...baseTail,
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
const menu = ref();
|
||||
|
||||
const showContextMenu = (event: Event) => {
|
||||
@ -177,7 +214,7 @@ const tryStart = () => {
|
||||
v-else-if="startStatus === 'preparing'"
|
||||
disabled
|
||||
icon="pi pi-spin pi-spinner"
|
||||
label="START"
|
||||
:label="t('start.button.start')"
|
||||
aria-label="start"
|
||||
size="small"
|
||||
class="m-2.5"
|
||||
@ -186,7 +223,7 @@ const tryStart = () => {
|
||||
v-else
|
||||
:disabled="false"
|
||||
icon="pi pi-ban"
|
||||
label="STOP"
|
||||
:label="t('start.button.stop')"
|
||||
aria-label="stop"
|
||||
size="small"
|
||||
class="m-2.5"
|
||||
|
@ -12,6 +12,9 @@ import { invoke } from '../../invoke';
|
||||
import { usePkgStore, usePrfStore } from '../../stores';
|
||||
import { Feature } from '../../types';
|
||||
import { hasFeature, pkgKey } from '../../util';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const pkgs = usePkgStore();
|
||||
const prf = usePrfStore();
|
||||
@ -60,14 +63,18 @@ load();
|
||||
<template>
|
||||
<OptionCategory title="Aime">
|
||||
<OptionRow
|
||||
title="Aime type"
|
||||
tooltip="Additional Aime plugins can be downloaded from the package store."
|
||||
:title="t('cfg.aime.type')"
|
||||
:tooltip="
|
||||
t('cfg.segatools.installTooltip', {
|
||||
thing: t('cfg.aime.modules'),
|
||||
})
|
||||
"
|
||||
>
|
||||
<Select
|
||||
v-model="prf.current!.data.sgt.aime"
|
||||
:options="[
|
||||
{ title: 'hardware', value: 'Disabled' },
|
||||
{ title: 'segatools built-in emulation', value: 'BuiltIn' },
|
||||
{ title: t('cfg.hardware'), value: 'Disabled' },
|
||||
{ title: t('cfg.segatools.builtIn'), value: 'BuiltIn' },
|
||||
...pkgs.byFeature(Feature.Aime).map((p) => {
|
||||
return {
|
||||
title: pkgKey(p),
|
||||
@ -83,8 +90,8 @@ load();
|
||||
></Select>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Aime code"
|
||||
tooltip="Only applicable with the segatools built-in emulation or with compatible third-party packages"
|
||||
:title="t('cfg.aime.code')"
|
||||
:tooltip="t('cfg.aime.codeTooltip')"
|
||||
v-if="prf.current!.data.sgt.aime !== 'Disabled'"
|
||||
>
|
||||
<InputText
|
||||
@ -97,7 +104,7 @@ load();
|
||||
/>
|
||||
</OptionRow>
|
||||
<div v-if="prf.current!.data.sgt.aime?.hasOwnProperty('AMNet')">
|
||||
<OptionRow title="Server name">
|
||||
<OptionRow :title="t('cfg.aime.serverName')">
|
||||
<InputText
|
||||
class="shrink"
|
||||
size="small"
|
||||
@ -106,7 +113,7 @@ load();
|
||||
v-model="prf.current!.data.sgt.amnet.name"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow title="Server address">
|
||||
<OptionRow :title="t('cfg.network.address')">
|
||||
<InputText
|
||||
class="shrink"
|
||||
size="small"
|
||||
@ -116,22 +123,21 @@ load();
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Use AiMeDB for physical cards"
|
||||
tooltip="Whether physical cards should use AiMeDB to retrieve access codes. If the game is using a hosted network, enable this option to load the same account data/profile as you would get on a physical cab."
|
||||
:title="t('cfg.aime.aimedb')"
|
||||
:tooltip="t('cfg.aime.aimedbTooltip')"
|
||||
>
|
||||
<ToggleSwitch v-model="prf.current!.data.sgt.amnet.physical" />
|
||||
</OptionRow>
|
||||
</div>
|
||||
<OptionRow
|
||||
title="Aime serial port"
|
||||
tooltip="Ports can be checked in Devices and Printers or at googlechromelabs.github.io/serial-terminal
|
||||
For AIC Pico, the AIME port should be selected."
|
||||
:title="t('cfg.aime.serialPort')"
|
||||
:tooltip="t('cfg.aime.serialPortTooltip')"
|
||||
v-if="prf.current!.data.sgt.aime === 'Disabled'"
|
||||
>
|
||||
<Select
|
||||
v-model="prf.current!.data.sgt.aime_port"
|
||||
:options="[
|
||||
{ title: 'default', value: null },
|
||||
{ title: t('default'), value: null },
|
||||
...Object.entries(coms ?? {}).map(([title, value]) => {
|
||||
return {
|
||||
title,
|
||||
@ -139,7 +145,7 @@ load();
|
||||
};
|
||||
}),
|
||||
]"
|
||||
placeholder="default"
|
||||
:placeholder="t('default')"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
></Select>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { Ref, computed, ref } from 'vue';
|
||||
import { Ref, computed, onMounted, ref } from 'vue';
|
||||
import InputNumber from 'primevue/inputnumber';
|
||||
import Select from 'primevue/select';
|
||||
import SelectButton from 'primevue/selectbutton';
|
||||
@ -8,11 +8,14 @@ import OptionCategory from '../OptionCategory.vue';
|
||||
import OptionRow from '../OptionRow.vue';
|
||||
import { invoke } from '../../invoke';
|
||||
import { usePrfStore } from '../../stores';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const capabilities: Ref<string[]> = ref([]);
|
||||
const displayList: Ref<{ title: string; value: string }[]> = ref([
|
||||
{
|
||||
title: 'Primary',
|
||||
title: t('cfg.display.primary'),
|
||||
value: 'default',
|
||||
},
|
||||
]);
|
||||
@ -25,7 +28,7 @@ const extraDisplayOptionsDisabled = computed(() => {
|
||||
const loadDisplays = () => {
|
||||
const newList = [
|
||||
{
|
||||
title: 'Primary',
|
||||
title: t('cfg.display.primary'),
|
||||
value: 'default',
|
||||
},
|
||||
];
|
||||
@ -62,19 +65,23 @@ const loadDisplays = () => {
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
loadDisplays();
|
||||
onMounted(() => {
|
||||
loadDisplays();
|
||||
});
|
||||
|
||||
const game = prf.current!.meta.game;
|
||||
const isVertical = game === 'ongeki';
|
||||
const adjustableRez = game === 'ongeki';
|
||||
const canSkipPrimarySwitch = game === 'ongeki';
|
||||
const game = computed(() => prf.current!.meta.game);
|
||||
const isVertical = computed(() => prf.current!.meta.game === 'ongeki');
|
||||
const adjustableRez = computed(() => prf.current!.meta.game === 'ongeki');
|
||||
const canSkipPrimarySwitch = computed(
|
||||
() => prf.current!.meta.game === 'ongeki'
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<OptionCategory title="Display">
|
||||
<OptionCategory :title="t('cfg.display.title')">
|
||||
<OptionRow
|
||||
v-if="capabilities.includes('display')"
|
||||
title="Target display"
|
||||
:title="t('cfg.display.target')"
|
||||
>
|
||||
<Select
|
||||
v-model="prf.current!.data.display.target"
|
||||
@ -87,7 +94,7 @@ const canSkipPrimarySwitch = game === 'ongeki';
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
class="number-input"
|
||||
title="Game resolution"
|
||||
:title="t('cfg.display.resolution')"
|
||||
v-if="adjustableRez"
|
||||
>
|
||||
<InputNumber
|
||||
@ -108,13 +115,13 @@ const canSkipPrimarySwitch = game === 'ongeki';
|
||||
v-model="prf.current!.data.display.rez[1]"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow title="Display mode">
|
||||
<OptionRow :title="t('cfg.display.mode')">
|
||||
<SelectButton
|
||||
v-model="prf.current!.data.display.mode"
|
||||
:options="[
|
||||
{ title: 'Window', value: 'Window' },
|
||||
{ title: 'Borderless window', value: 'Borderless' },
|
||||
{ title: 'Fullscreen', value: 'Fullscreen' },
|
||||
{ title: t('cfg.display.window'), value: 'Window' },
|
||||
{ title: t('cfg.display.borderless'), value: 'Borderless' },
|
||||
{ title: t('cfg.display.fullscreen'), value: 'Fullscreen' },
|
||||
]"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
@ -122,7 +129,7 @@ const canSkipPrimarySwitch = game === 'ongeki';
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Display rotation"
|
||||
:title="t('cfg.display.rotation')"
|
||||
v-if="capabilities.includes('display')"
|
||||
>
|
||||
<SelectButton
|
||||
@ -130,12 +137,18 @@ const canSkipPrimarySwitch = game === 'ongeki';
|
||||
:options="
|
||||
isVertical
|
||||
? [
|
||||
{ title: 'Portrait', value: 90 },
|
||||
{ title: 'Portrait (flipped)', value: 270 },
|
||||
{ title: t('cfg.display.portrait'), value: 90 },
|
||||
{
|
||||
title: `${t('cfg.display.portrait')} (${t('cfg.display.flipped')})`,
|
||||
value: 270,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{ title: 'Landscape', value: 0 },
|
||||
{ title: 'Landscape (flipped)', value: 180 },
|
||||
{ title: t('cfg.display.landscape'), value: 0 },
|
||||
{
|
||||
title: `${t('cfg.display.landscape')} (${t('cfg.display.flipped')})`,
|
||||
value: 180,
|
||||
},
|
||||
]
|
||||
"
|
||||
:allow-empty="true"
|
||||
@ -147,7 +160,7 @@ const canSkipPrimarySwitch = game === 'ongeki';
|
||||
<OptionRow
|
||||
v-if="capabilities.includes('display')"
|
||||
class="number-input"
|
||||
title="Refresh Rate"
|
||||
:title="t('cfg.display.refreshRate')"
|
||||
>
|
||||
<InputNumber
|
||||
v-if="game === 'ongeki'"
|
||||
@ -173,9 +186,9 @@ const canSkipPrimarySwitch = game === 'ongeki';
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Borderless fullscreen"
|
||||
:title="t('cfg.display.borderlessFullscreen')"
|
||||
v-if="capabilities.includes('display')"
|
||||
tooltip="Match display resolution with the game."
|
||||
:tooltip="t('cfg.display.borderlessFullscreenTooltip')"
|
||||
>
|
||||
<ToggleSwitch
|
||||
:disabled="
|
||||
@ -186,28 +199,31 @@ const canSkipPrimarySwitch = game === 'ongeki';
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Skip switching primary display"
|
||||
:title="t('cfg.display.dontSwitchPrimary')"
|
||||
v-if="
|
||||
capabilities.includes('display') &&
|
||||
prf.current?.data.display.target !== 'default' &&
|
||||
(prf.current!.data.display.dont_switch_primary ||
|
||||
displayList.length > 2) &&
|
||||
canSkipPrimarySwitch
|
||||
!capabilities.includes('display') ||
|
||||
(prf.current?.data.display.target !== 'default' &&
|
||||
(prf.current!.data.display.dont_switch_primary ||
|
||||
displayList.length > 2) &&
|
||||
canSkipPrimarySwitch)
|
||||
"
|
||||
dangerous-tooltip="Only enable this option if switching the primary display causes issues. The monitors must have a matching refresh rate."
|
||||
:dangerous-tooltip="t('cfg.display.dontSwitchPrimaryTooltip')"
|
||||
>
|
||||
<ToggleSwitch
|
||||
:disabled="extraDisplayOptionsDisabled"
|
||||
:disabled="
|
||||
extraDisplayOptionsDisabled &&
|
||||
capabilities.includes('display')
|
||||
"
|
||||
v-model="prf.current!.data.display.dont_switch_primary"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Display index"
|
||||
:title="t('cfg.display.index')"
|
||||
class="number-input"
|
||||
v-if="
|
||||
capabilities.includes('display') &&
|
||||
prf.current?.data.display.target !== 'default' &&
|
||||
prf.current!.data.display.dont_switch_primary
|
||||
!capabilities.includes('display') ||
|
||||
(prf.current?.data.display.target !== 'default' &&
|
||||
prf.current!.data.display.dont_switch_primary)
|
||||
"
|
||||
>
|
||||
<InputNumber
|
||||
@ -216,8 +232,12 @@ const canSkipPrimarySwitch = game === 'ongeki';
|
||||
:min="game === 'chunithm' ? 0 : 1"
|
||||
:max="32"
|
||||
:use-grouping="false"
|
||||
placeholder="1"
|
||||
v-model="prf.current!.data.display.monitor_index_override"
|
||||
:disabled="extraDisplayOptionsDisabled"
|
||||
:disabled="
|
||||
extraDisplayOptionsDisabled &&
|
||||
capabilities.includes('display')
|
||||
"
|
||||
:allow-empty="true"
|
||||
/>
|
||||
</OptionRow>
|
||||
|
@ -5,27 +5,27 @@ import KeyboardKey from '../KeyboardKey.vue';
|
||||
import OptionCategory from '../OptionCategory.vue';
|
||||
import OptionRow from '../OptionRow.vue';
|
||||
import { usePrfStore } from '../../stores';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const prf = usePrfStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<OptionCategory title="Keyboard">
|
||||
<OptionRow
|
||||
title="Enable"
|
||||
tooltip="Only applicable if the IO module is set to segatools built-in (keyboard) or a compatible third-party module (like mu3io.NET)"
|
||||
>
|
||||
<OptionCategory :title="t('cfg.keyboard.title')">
|
||||
<OptionRow :title="t('enable')" :tooltip="t('cfg.keyboard.tooltip')">
|
||||
<ToggleSwitch v-model="prf.current!.data.keyboard!.data.enabled" />
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Lever mode"
|
||||
:title="t('cfg.keyboard.leverMode')"
|
||||
v-if="prf.current!.data.keyboard!.game === 'Ongeki'"
|
||||
>
|
||||
<SelectButton
|
||||
v-model="prf.current!.data.keyboard!.data.use_mouse"
|
||||
:options="[
|
||||
{ title: 'XInput', value: false },
|
||||
{ title: 'Mouse', value: true },
|
||||
{ title: t('cfg.keyboard.mouse'), value: true },
|
||||
]"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
@ -95,7 +95,7 @@ const prf = usePrfStore();
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="prf.current?.meta.game === 'chunithm'">
|
||||
<div class="absolute left-1/2 top-1/5">
|
||||
<div class="absolute left-9/17 top-1/12">
|
||||
<div
|
||||
class="flex flex-row flex-nowrap gap-2 self-center w-full"
|
||||
>
|
||||
@ -108,6 +108,7 @@ const prf = usePrfStore();
|
||||
button="ir"
|
||||
:index="idx - 1"
|
||||
:tooltip="`ir${idx}`"
|
||||
tall
|
||||
small
|
||||
color="rgba(0, 255, 0, 0.2)"
|
||||
/>
|
||||
|
@ -4,18 +4,24 @@ import FileEditor from '../FileEditor.vue';
|
||||
import OptionCategory from '../OptionCategory.vue';
|
||||
import OptionRow from '../OptionRow.vue';
|
||||
import { usePrfStore } from '../../stores';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const prf = usePrfStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<OptionCategory title="Misc">
|
||||
<OptionRow title="OpenSSL bug workaround for Intel ≥10th gen">
|
||||
<OptionCategory :title="t('cfg.misc.title')">
|
||||
<OptionRow
|
||||
:title="t('cfg.misc.intel')"
|
||||
:dangerous-tooltip="t('cfg.misc.intelTooltip')"
|
||||
>
|
||||
<ToggleSwitch v-model="prf.current!.data.sgt.intel" />
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="More segatools options"
|
||||
tooltip="Advanced options not covered by STARTLINER"
|
||||
:title="t('cfg.misc.other')"
|
||||
:tooltip="t('cfg.misc.otherTooltip')"
|
||||
>
|
||||
<!-- <Button icon="pi pi-refresh" size="small" /> -->
|
||||
<FileEditor filename="segatools-base.ini" />
|
||||
|
@ -6,18 +6,21 @@ import FilePicker from '../FilePicker.vue';
|
||||
import OptionCategory from '../OptionCategory.vue';
|
||||
import OptionRow from '../OptionRow.vue';
|
||||
import { usePrfStore } from '../../stores';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const prf = usePrfStore();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<OptionCategory title="Network">
|
||||
<OptionRow title="Network type">
|
||||
<OptionCategory :title="t('cfg.network.title')">
|
||||
<OptionRow :title="t('cfg.network.type')">
|
||||
<SelectButton
|
||||
v-model="prf.current!.data.network.network_type"
|
||||
:options="[
|
||||
{ title: 'Remote', value: 'Remote' },
|
||||
{ title: 'Local (ARTEMiS)', value: 'Artemis' },
|
||||
{ title: t('cfg.network.remote'), value: 'Remote' },
|
||||
{ title: t('cfg.network.localArtemis'), value: 'Artemis' },
|
||||
]"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
@ -25,8 +28,8 @@ const prf = usePrfStore();
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
v-if="prf.current!.data.network.network_type == 'Artemis'"
|
||||
title="ARTEMiS path"
|
||||
v-if="prf.current!.data.network.network_type === 'Artemis'"
|
||||
:title="t('cfg.network.artemisPath')"
|
||||
>
|
||||
<FilePicker
|
||||
:directory="false"
|
||||
@ -47,7 +50,7 @@ const prf = usePrfStore();
|
||||
</OptionRow> -->
|
||||
<OptionRow
|
||||
v-if="prf.current!.data.network.network_type == 'Remote'"
|
||||
title="Server address"
|
||||
:title="t('cfg.network.address')"
|
||||
>
|
||||
<InputText
|
||||
class="shrink"
|
||||
@ -58,7 +61,7 @@ const prf = usePrfStore();
|
||||
/> </OptionRow
|
||||
><OptionRow
|
||||
v-if="prf.current!.data.network.network_type == 'Remote'"
|
||||
title="Keychip"
|
||||
:title="t('cfg.network.keychip')"
|
||||
>
|
||||
<InputText
|
||||
class="shrink"
|
||||
@ -67,7 +70,7 @@ const prf = usePrfStore();
|
||||
placeholder="A123-01234567890"
|
||||
v-model="prf.current!.data.network.keychip"
|
||||
/> </OptionRow
|
||||
><OptionRow title="Subnet">
|
||||
><OptionRow :title="t('cfg.network.subnet')">
|
||||
<InputText
|
||||
class="shrink"
|
||||
size="small"
|
||||
@ -76,7 +79,7 @@ const prf = usePrfStore();
|
||||
v-model="prf.current!.data.network.subnet"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow title="Address suffix">
|
||||
<OptionRow :title="t('cfg.network.addrSuffix')">
|
||||
<InputNumber
|
||||
class="shrink"
|
||||
size="small"
|
||||
|
@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { Ref, computed, ref } from 'vue';
|
||||
import Select from 'primevue/select';
|
||||
import { useConfirm } from 'primevue/useconfirm';
|
||||
import { emit } from '@tauri-apps/api/event';
|
||||
@ -11,11 +11,22 @@ import { invoke } from '../../invoke';
|
||||
import { usePkgStore, usePrfStore } from '../../stores';
|
||||
import { Feature } from '../../types';
|
||||
import { pkgKey } from '../../util';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const prf = usePrfStore();
|
||||
const pkgs = usePkgStore();
|
||||
const confirmDialog = useConfirm();
|
||||
|
||||
const capabilities: Ref<string[]> = ref([]);
|
||||
|
||||
invoke('list_platform_capabilities').then(async (v: unknown) => {
|
||||
if (Array.isArray(v)) {
|
||||
capabilities.value.push(...v);
|
||||
}
|
||||
});
|
||||
|
||||
const names = computed(() => {
|
||||
switch (prf.current?.meta.game) {
|
||||
case 'ongeki': {
|
||||
@ -32,8 +43,6 @@ const names = computed(() => {
|
||||
io: 'chuniio',
|
||||
};
|
||||
}
|
||||
case undefined:
|
||||
throw new Error('Option tab without a profile');
|
||||
}
|
||||
});
|
||||
|
||||
@ -54,16 +63,16 @@ const checkSegatoolsIni = async (target: string) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<OptionCategory title="General">
|
||||
<OptionCategory :title="t('cfg.segatools.general')">
|
||||
<OptionRow
|
||||
:title="names.exe"
|
||||
tooltip="STARTLINER expects unpacked executables put into otherwise clean data."
|
||||
:title="names?.exe"
|
||||
:tooltip="t('cfg.segatools.targetTooltip')"
|
||||
>
|
||||
<FilePicker
|
||||
:directory="false"
|
||||
:promptname="names.exe"
|
||||
:promptname="names?.exe"
|
||||
extension="exe"
|
||||
:value="prf.current!.data.sgt.target"
|
||||
:value="prf.current?.data.sgt.target"
|
||||
:callback="
|
||||
(value: string) => (
|
||||
(prf.current!.data.sgt.target = value),
|
||||
@ -77,7 +86,7 @@ const checkSegatoolsIni = async (target: string) => {
|
||||
<FilePicker
|
||||
:directory="true"
|
||||
placeholder="amfs"
|
||||
:value="prf.current!.data.sgt.amfs"
|
||||
:value="prf.current?.data.sgt.amfs"
|
||||
:callback="
|
||||
(value: string) => (prf.current!.data.sgt.amfs = value)
|
||||
"
|
||||
@ -103,8 +112,12 @@ const checkSegatoolsIni = async (target: string) => {
|
||||
></FilePicker>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
:title="names.hook"
|
||||
tooltip="Hooks can be downloaded from the package store."
|
||||
:title="names?.hook"
|
||||
:tooltip="
|
||||
t('cfg.segatools.installTooltip', {
|
||||
thing: t('cfg.segatools.hooks'),
|
||||
})
|
||||
"
|
||||
>
|
||||
<Select
|
||||
v-model="prf.current!.data.sgt.hook"
|
||||
@ -125,15 +138,18 @@ const checkSegatoolsIni = async (target: string) => {
|
||||
></Select>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
:title="names.io"
|
||||
tooltip="IO plugins can be downloaded from the package store."
|
||||
:title="names?.io"
|
||||
:tooltip="`${t('cfg.segatools.ioModulesDesc')}
|
||||
${t('cfg.segatools.installTooltip', {
|
||||
thing: t('cfg.segatools.ioModules'),
|
||||
})}`"
|
||||
>
|
||||
<Select
|
||||
v-model="prf.current!.data.sgt.io2"
|
||||
:options="[
|
||||
{ title: 'native io4', value: 'hardware' },
|
||||
{ title: t('cfg.segatools.io4'), value: 'hardware' },
|
||||
{
|
||||
title: 'segatools built-in (keyboard)',
|
||||
title: t('cfg.segatools.ioBuiltIn'),
|
||||
value: 'segatools_built_in',
|
||||
},
|
||||
...pkgs
|
||||
@ -153,5 +169,29 @@ const checkSegatoolsIni = async (target: string) => {
|
||||
option-value="value"
|
||||
></Select>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
v-if="capabilities.includes('wine')"
|
||||
:title="t('cfg.wine.runtime')"
|
||||
>
|
||||
<FilePicker
|
||||
:directory="false"
|
||||
:value="prf.current!.data.wine.runtime"
|
||||
:callback="
|
||||
(value: string) => (prf.current!.data.wine.runtime = value)
|
||||
"
|
||||
></FilePicker>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
v-if="capabilities.includes('wine')"
|
||||
:title="t('cfg.wine.prefix')"
|
||||
>
|
||||
<FilePicker
|
||||
:directory="true"
|
||||
:value="prf.current!.data.wine.prefix"
|
||||
:callback="
|
||||
(value: string) => (prf.current!.data.wine.prefix = value)
|
||||
"
|
||||
></FilePicker>
|
||||
</OptionRow>
|
||||
</OptionCategory>
|
||||
</template>
|
||||
|
@ -1,92 +1,43 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import SelectButton from 'primevue/selectbutton';
|
||||
import ToggleSwitch from 'primevue/toggleswitch';
|
||||
import OptionCategory from '../OptionCategory.vue';
|
||||
import OptionRow from '../OptionRow.vue';
|
||||
import { useClientStore } from '../../stores';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const client = useClientStore();
|
||||
|
||||
const offlineModel = computed({
|
||||
get() {
|
||||
return client.offlineMode;
|
||||
},
|
||||
async set(value: boolean) {
|
||||
await client.setOfflineMode(value);
|
||||
},
|
||||
});
|
||||
|
||||
const updatesModel = computed({
|
||||
get() {
|
||||
return client.enableAutoupdates;
|
||||
},
|
||||
async set(value: boolean) {
|
||||
await client.setAutoupdates(value);
|
||||
},
|
||||
});
|
||||
|
||||
const verboseModel = computed({
|
||||
get() {
|
||||
return client.verbose;
|
||||
},
|
||||
async set(value: boolean) {
|
||||
await client.setVerbose(value);
|
||||
},
|
||||
});
|
||||
|
||||
const themeModel = computed({
|
||||
get() {
|
||||
return client.theme;
|
||||
},
|
||||
async set(value: 'light' | 'dark' | 'system') {
|
||||
await client.setTheme(value);
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<OptionCategory title="STARTLINER">
|
||||
<OptionRow title="UI scaling">
|
||||
<SelectButton
|
||||
v-model="client.scaleModel"
|
||||
:options="[
|
||||
{ title: 'S', value: 's' },
|
||||
{ title: 'M', value: 'm' },
|
||||
{ title: 'L', value: 'l' },
|
||||
{ title: 'XL', value: 'xl' },
|
||||
]"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
<OptionRow
|
||||
:title="t('cfg.startliner.offlineMode')"
|
||||
:tooltip="`${t('cfg.startliner.offlineModeTooltip')} ${t('cfg.afterRestart')}`"
|
||||
>
|
||||
<ToggleSwitch
|
||||
:model-value="client.offlineMode"
|
||||
@update:model-value="
|
||||
async (v) => await client.setOfflineMode(v)
|
||||
"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Offline mode"
|
||||
tooltip="Disables the package store. Applies after a restart."
|
||||
>
|
||||
<ToggleSwitch v-model="offlineModel" />
|
||||
</OptionRow>
|
||||
<OptionRow title="Enable automatic updates">
|
||||
<ToggleSwitch v-model="updatesModel" />
|
||||
<OptionRow :title="t('cfg.startliner.autoUpdate')">
|
||||
<ToggleSwitch
|
||||
:model-value="client.enableAutoupdates"
|
||||
@update:model-value="
|
||||
async (v) => await client.setAutoupdates(v)
|
||||
"
|
||||
></ToggleSwitch>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Enable detailed logs"
|
||||
tooltip="Applies after a restart."
|
||||
:title="t('cfg.startliner.verbose')"
|
||||
:tooltip="t('cfg.afterRestart')"
|
||||
>
|
||||
<ToggleSwitch v-model="verboseModel" />
|
||||
</OptionRow>
|
||||
<OptionRow title="Theme">
|
||||
<SelectButton
|
||||
v-model="themeModel"
|
||||
:options="[
|
||||
{ title: 'System', value: 'system' },
|
||||
{ title: 'Light', value: 'light' },
|
||||
{ title: 'Dark', value: 'dark' },
|
||||
]"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
<ToggleSwitch
|
||||
:model-value="client.verbose"
|
||||
@update:model-value="async (v) => await client.setVerbose(v)"
|
||||
/>
|
||||
</OptionRow>
|
||||
</OptionCategory>
|
||||
|
27
src/i18n.ts
Normal file
27
src/i18n.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import en from './i18n/en';
|
||||
import { createI18n } from 'vue-i18n';
|
||||
|
||||
export type Locale = 'en' | 'ja' | 'pl';
|
||||
|
||||
const loadLocaleMessages = async (locale: Locale) => {
|
||||
return (await import(`./i18n/${locale}.ts`)).default;
|
||||
};
|
||||
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: 'en',
|
||||
fallbackLocale: 'en',
|
||||
warnHtmlInMessage: false,
|
||||
warnHtmlMessage: false,
|
||||
messages: { en, ja: {}, pl: {} },
|
||||
});
|
||||
|
||||
const setLocale = async (locale: Locale) => {
|
||||
if (locale !== 'en') {
|
||||
const messages = await loadLocaleMessages(locale);
|
||||
i18n.global.setLocaleMessage(locale, messages);
|
||||
}
|
||||
i18n.global.locale.value = locale;
|
||||
};
|
||||
|
||||
export { i18n, setLocale };
|
248
src/i18n/en.ts
Normal file
248
src/i18n/en.ts
Normal file
@ -0,0 +1,248 @@
|
||||
export default {
|
||||
ok: 'OK',
|
||||
cancel: 'Cancel',
|
||||
enable: 'Enable',
|
||||
disable: 'Disable',
|
||||
default: 'Default',
|
||||
search: 'Search',
|
||||
next: 'Next',
|
||||
skip: 'Skip',
|
||||
close: 'Close',
|
||||
by: 'by {namespace}',
|
||||
start: {
|
||||
failed: 'Start check failed',
|
||||
accept: 'Run anyway',
|
||||
error: {
|
||||
package: 'Package missing',
|
||||
dependency: 'Dependency missing',
|
||||
tool: 'Tool missing',
|
||||
unknown: 'Unknown error',
|
||||
},
|
||||
tooltip: {
|
||||
game: 'The game path must be specified',
|
||||
amfs: 'The amfs path must be specified',
|
||||
segatools: 'A segatools hook package is necessary',
|
||||
},
|
||||
button: {
|
||||
start: 'START',
|
||||
stop: 'STOP',
|
||||
unchecked: 'Skip checks and start',
|
||||
shortcut: 'Create desktop shortcut',
|
||||
help: 'Help',
|
||||
refresh: 'Reapply mods and start',
|
||||
cache: 'Clear mod cache',
|
||||
},
|
||||
},
|
||||
game: {
|
||||
ongeki: 'O.N.G.E.K.I.',
|
||||
chunithm: 'CHUNITHM',
|
||||
},
|
||||
profile: {
|
||||
welcome: 'Welcome to STARTLINER! Start by creating a profile.',
|
||||
create: '{game} profile',
|
||||
delete: 'Delete profile',
|
||||
reallyDelete: 'Are you sure you want to delete {profile}?',
|
||||
template: 'STARTLINER template',
|
||||
importTemplate: 'Import template',
|
||||
exportTemplate: 'Export template',
|
||||
export: 'Export',
|
||||
},
|
||||
creator: {
|
||||
header: 'Package creator',
|
||||
basic: 'Basic information',
|
||||
name: 'Name',
|
||||
description: 'Description',
|
||||
website: 'Website',
|
||||
type: 'Package type',
|
||||
rainy: 'Standard',
|
||||
segatools: 'Segatools',
|
||||
native: 'Native',
|
||||
games: 'Games',
|
||||
packageFormat: 'Package format spec',
|
||||
},
|
||||
store: {
|
||||
installRecommended: 'Install recommended packages',
|
||||
installed: 'Show installed',
|
||||
deprecated: 'Show deprecated',
|
||||
nsfw: 'Show NSFW',
|
||||
incompatible: 'This package is currently incompatible with STARTLINER.',
|
||||
|
||||
includeCategories: 'Include categories',
|
||||
excludeCategories: 'Exclude categories',
|
||||
},
|
||||
pkglist: {
|
||||
missing: 'Missing',
|
||||
local: 'Local packages',
|
||||
namespace: 'By namespace',
|
||||
type: 'By type',
|
||||
category: 'By category',
|
||||
standard: 'Standard mods',
|
||||
native: 'Native mods',
|
||||
segatools: 'segatools',
|
||||
unsupported: 'Unsupported',
|
||||
exclusions: 'Exclusions:',
|
||||
},
|
||||
patch: {
|
||||
loading: 'Loading...',
|
||||
noneFound:
|
||||
"No compatible patches found. Make sure you're using unpacked and unpatched files.",
|
||||
forceLoad: 'Force load',
|
||||
// Example patch name override
|
||||
// 'standard-no-encryption': 'No encryption',
|
||||
// 'standard-no-encryption-tooltip': 'Will also disable TLS',
|
||||
// It is also possible to add a tooltip where there normally is none
|
||||
// 'standard-maximum-tracks-tooltip': 'The number of tracks per credit',
|
||||
// For more info check https://gitea.tendokyu.moe/akanyan/STARTLINER/wiki/Translation-%26-Localization
|
||||
},
|
||||
cfg: {
|
||||
afterRestart: 'Applied after a restart',
|
||||
hardware: 'Hardware',
|
||||
segatools: {
|
||||
general: 'General',
|
||||
builtIn: 'Segatools built-in emulation',
|
||||
targetTooltip:
|
||||
'STARTLINER expects unpacked executables put into otherwise clean data.',
|
||||
hooks: 'Hooks',
|
||||
ioModules: 'IO modules',
|
||||
ioModulesDesc: 'This should match your desired input method.',
|
||||
ioBuiltIn: 'segatools built-in (keyboard)',
|
||||
io4: 'Native IO4',
|
||||
installTooltip: '{thing} can be downloaded from the package store.',
|
||||
},
|
||||
display: {
|
||||
title: 'Display',
|
||||
resolution: 'Game resolution',
|
||||
primary: 'Primary',
|
||||
target: 'Target display',
|
||||
mode: 'Mode',
|
||||
rotation: 'Rotation',
|
||||
refreshRate: 'Refresh rate',
|
||||
borderlessFullscreen: 'Borderless fullscreen',
|
||||
borderlessFullscreenTooltip:
|
||||
'Match display resolution with the game.',
|
||||
dontSwitchPrimary: 'Skip switching primary display',
|
||||
dontSwitchPrimaryTooltip:
|
||||
'Only enable this option if switching the primary display causes issues. The monitors must have a matching refresh rate.',
|
||||
index: 'Display index',
|
||||
portrait: 'Portrait',
|
||||
landscape: 'Landscape',
|
||||
flipped: 'flipped',
|
||||
window: 'Window',
|
||||
borderless: 'Borderless window',
|
||||
fullscreen: 'Fullscreen',
|
||||
},
|
||||
network: {
|
||||
title: 'Network',
|
||||
type: 'Network type',
|
||||
remote: 'Remote',
|
||||
localArtemis: 'Local (ARTEMiS)',
|
||||
artemisPath: 'ARTEMiS path',
|
||||
address: 'Server address',
|
||||
keychip: 'Keychip',
|
||||
subnet: 'Subnet',
|
||||
addrSuffix: 'Address suffix',
|
||||
},
|
||||
aime: {
|
||||
type: 'Aime type',
|
||||
modules: 'Aime modules',
|
||||
code: 'Aime code',
|
||||
codeTooltip:
|
||||
'Only applicable with the segatools built-in emulation or with compatible third-party packages',
|
||||
aimedb: 'Use AiMeDB for physical cards',
|
||||
aimedbTooltip:
|
||||
'Whether physical cards should use AiMeDB to retrieve access codes. If the game is using a hosted network, enable this option to load the same account data/profile as you would get on a physical cab.',
|
||||
serialPort: 'Aime serial port',
|
||||
serialPortTooltip: `Ports can be checked in Devices and Printers or at googlechromelabs.github.io/serial-terminal
|
||||
For AIC Pico, the AIME port should be selected.`,
|
||||
serverName: 'Server name',
|
||||
},
|
||||
misc: {
|
||||
title: 'Miscellaneous',
|
||||
intel: 'OpenSSL bug workaround for Intel ≥10th gen',
|
||||
intelTooltip: 'It is recommended to patch amdaemon instead.',
|
||||
other: 'Other segatools options',
|
||||
otherTooltip:
|
||||
'Advanced or situational options not covered by STARTLINER',
|
||||
},
|
||||
extensions: {
|
||||
title: 'Extensions',
|
||||
bepInExConsole: 'BepInEx console',
|
||||
audioMode: 'Audio mode',
|
||||
audioTooltip:
|
||||
'Exclusive 2-channel mode requires 7EVENDAYSHOLIDAYS-ExclusiveAudio',
|
||||
audioShared: 'Shared',
|
||||
audio6Ch: 'Exclusive 6-channel',
|
||||
audio2Ch: 'Exclusive 2-channel',
|
||||
sampleRate: 'Sample rate',
|
||||
blacklist: 'Song ID blacklist',
|
||||
blacklistTooltip:
|
||||
'Scores on charts within this ID range will not be saved nor uploaded',
|
||||
bonusTracks: 'Unlock bonus tracks',
|
||||
bonusTracksTooltip:
|
||||
'Disabling this option can help declutter the song list',
|
||||
saekawa: 'Saekawa configuration file',
|
||||
inohara: 'Inohara configuration file',
|
||||
},
|
||||
keyboard: {
|
||||
title: 'Keyboard',
|
||||
tooltip:
|
||||
'Only applicable if the IO module is set to segatools built-in (keyboard) or a compatible third-party module (like mu3io.NET)',
|
||||
leverMode: 'Lever mode',
|
||||
mouse: 'Mouse',
|
||||
irTooltip:
|
||||
'When playing on an actual keyboard, only bind ir1; leave the rest unbound',
|
||||
},
|
||||
wine: {
|
||||
prefix: 'Wine prefix',
|
||||
runtime: 'Wine runtime',
|
||||
},
|
||||
startliner: {
|
||||
offlineMode: 'Offline mode',
|
||||
offlineModeTooltip: 'Disables the package store.',
|
||||
autoUpdate: 'Automatic updates',
|
||||
verbose: 'Detailed logs',
|
||||
},
|
||||
},
|
||||
onboarding: {
|
||||
or: 'or',
|
||||
backButton: 'a button on the back of the controller',
|
||||
standard: `
|
||||
You might get stuck on the following screen:
|
||||
|
||||
{bigblack}Aグループの基準機から設定を取得{endbig}
|
||||
|
||||
In which case, you should go to the test menu, and in game settings {black}ゲーム設定{end} switch from "follow the standard machine" {black}基準機に従う{end} to "standard machine" {black}基準機{end}.
|
||||
|
||||
The test menu can be accessed with %TESTMENU%.
|
||||
`,
|
||||
|
||||
'ongeki-system-processing': `
|
||||
You might get stuck on this screen for several minutes. _This is normal_. The game just takes a long time to load data.
|
||||
|
||||
If you install <code>7EVENDAYSHOLIDAYS/LoadBoost</code>, subsequent launches will be much faster.
|
||||
`,
|
||||
|
||||
'ongeki-lever': `
|
||||
You also have to calibrate the lever, or you may get the error 3301.
|
||||
|
||||
Go to lever settings ({black}レバー設定{end}), move the lever to both edges, then press "end" ({black}終了{end}) and "save" ({black}保存する{end}).
|
||||
`,
|
||||
|
||||
'chunithm-server': `
|
||||
If you're stuck on this screen, restart the game.
|
||||
|
||||
If the problem persists, {link}check your network configuration{endlink}
|
||||
`,
|
||||
|
||||
finale: `
|
||||
You can access this page any time by right-clicking the START button.
|
||||
|
||||
Additional resources:
|
||||
|
||||
- {segaguide}SEGAguide{endlink}
|
||||
- {twotorial}two-torial{endlink}
|
||||
|
||||
## Have fun
|
||||
`,
|
||||
},
|
||||
};
|
14
src/i18n/ja.ts
Normal file
14
src/i18n/ja.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export default {
|
||||
game: {
|
||||
ongeki: 'オンゲキ',
|
||||
chunithm: 'チュウニズム',
|
||||
},
|
||||
profile: {
|
||||
create: '{game}のプロフィル',
|
||||
},
|
||||
cfg: {
|
||||
aime: {
|
||||
type: 'Aimeタイプ',
|
||||
},
|
||||
},
|
||||
};
|
288
src/i18n/pl.ts
Normal file
288
src/i18n/pl.ts
Normal file
@ -0,0 +1,288 @@
|
||||
export default {
|
||||
ok: 'OK',
|
||||
cancel: 'Anuluj',
|
||||
enable: 'Włącz',
|
||||
disable: 'Wyłącz',
|
||||
default: 'Domyślne',
|
||||
search: 'Wyszukaj',
|
||||
next: 'Dalej',
|
||||
skip: 'Pomiń',
|
||||
close: 'Zamknij',
|
||||
by: 'od {namespace}',
|
||||
start: {
|
||||
failed: 'Uruchomienie nie powiodło się',
|
||||
accept: 'Uruchom mimo to',
|
||||
error: {
|
||||
package: 'Brakujący pakiet',
|
||||
dependency: 'Brakująca dependencja',
|
||||
tool: 'Brakujące narzędzie',
|
||||
unknown: 'Nieznany błąd',
|
||||
},
|
||||
tooltip: {
|
||||
game: 'Należy najpierw wskazać lokalizację gry',
|
||||
amfs: 'Należy najpierw wskazać lokalizację amfs',
|
||||
segatools: 'Należy dodać hook segatools',
|
||||
},
|
||||
button: {
|
||||
start: 'START',
|
||||
stop: 'STOP',
|
||||
unchecked: 'Uruchom bez sprawdzania',
|
||||
shortcut: 'Utwórz skrót',
|
||||
help: 'Pomoc',
|
||||
refresh: 'Uruchom po re-aplikacji modów',
|
||||
cache: 'Wyczyść mod cache',
|
||||
},
|
||||
},
|
||||
game: {
|
||||
ongeki: 'O.N.G.E.K.I.',
|
||||
chunithm: 'CHUNITHM',
|
||||
},
|
||||
profile: {
|
||||
welcome: 'Witaj w STARTLINERZE! Zacznij od utworzenia profilu',
|
||||
create: 'Profil {game}',
|
||||
delete: 'Usuń profil',
|
||||
reallyDelete: 'Czy na pewno chcesz usunąć {profile}?',
|
||||
template: 'Szablon',
|
||||
importTemplate: 'Importuj szablon',
|
||||
exportTemplate: 'Eksportuj szablon',
|
||||
export: 'Eksportuj',
|
||||
},
|
||||
creator: {
|
||||
header: 'Kreator pakietów',
|
||||
basic: 'Podstawowe informacje',
|
||||
name: 'Nazwa',
|
||||
description: 'Opis',
|
||||
website: 'Strona internetowa',
|
||||
type: 'Typ',
|
||||
rainy: 'Standardowy',
|
||||
segatools: 'Segatools',
|
||||
native: 'Natywny',
|
||||
games: 'Gry',
|
||||
packageFormat: 'Specyfikacja formatu',
|
||||
},
|
||||
store: {
|
||||
installRecommended: 'Dodaj zalecane pakiety',
|
||||
installed: 'Pokaż zainstalowane',
|
||||
deprecated: 'Pokaż przestarzałe',
|
||||
nsfw: 'Pokaż mityczny O.N.G.E.K.I. Sex Mod dlaczego ta opcja w ogóle tu jest',
|
||||
incompatible:
|
||||
'Ten pakiet jest obecnie niekompatybilny ze STARTLINEREM.',
|
||||
includeCategories: 'Włącz kategorie',
|
||||
excludeCategories: 'Wyłącz kategorie',
|
||||
},
|
||||
pkglist: {
|
||||
missing: 'Niedostępne',
|
||||
local: 'Lokalne pakiety',
|
||||
namespace: 'Po przestrzeni nazw',
|
||||
type: 'Po typie',
|
||||
category: 'Po kategorii',
|
||||
standard: 'Standardowe mody',
|
||||
native: 'Natywne mody',
|
||||
segatools: 'segatools',
|
||||
unsupported: 'Niewspierane',
|
||||
exclusions: 'Czarna lista:',
|
||||
},
|
||||
patch: {
|
||||
loading: 'Wczytuję...',
|
||||
noneFound:
|
||||
'Brak kompatybilnych łatek. Upewnij się, że używasz czystych odpakowanych plików.',
|
||||
forceLoad: 'Wymuś załadowanie',
|
||||
'standard-shared-audio':
|
||||
'Wymuś współdzielony tryb dźwięku; częstotliwość w systemie musi wynosić 48kHz',
|
||||
'standard-shared-audio-tooltip':
|
||||
'Poprawia kompatybilność, ale może zwiększyć opóźnienie',
|
||||
'standard-2ch': 'Wymuś stereo',
|
||||
'standard-2ch-tooltip': 'Może powodować bass overload',
|
||||
'standard-song-timer': 'Wyłącz timer wyboru utworu',
|
||||
'standard-map-timer': 'Timer wyboru mapy',
|
||||
'standard-map-timer-tooltip':
|
||||
'Jeśli ustawiony na wartość ujemną, timer wyniesie 968 + wartość (np. 968 + -1 = 967)',
|
||||
'standard-ticket-timer': 'Timer wyboru biletu',
|
||||
'standard-ticket-timer-tooltip':
|
||||
'Jeśli ustawiony na wartość ujemną, timer wyniesie 968 + wartość (np. 968 + -1 = 967)',
|
||||
'standard-course-timer': 'Timer wyboru dana',
|
||||
'standard-course-timer-tooltip':
|
||||
'Jeśli ustawiony na wartość ujemną, timer wyniesie 968 + wartość (np. 968 + -1 = 967)',
|
||||
'standard-unlimited-tracks': 'Nieograniczona maksymalna liczba utworów',
|
||||
'standard-unlimited-tracks-tooltip':
|
||||
'Konieczne do grania więcej niż 7 utworów na kredyt',
|
||||
'standard-maximum-tracks': 'Maksymalna liczba utworów',
|
||||
'standard-no-encryption': 'Wyłącz szyfrowanie',
|
||||
'standard-no-encryption-tooltip': 'Wyłączy również TLS',
|
||||
'standard-no-tls': 'Wyłącz TLS',
|
||||
'standard-no-tls-tooltip': 'Obejście problemów z serwerem tytułowym',
|
||||
'standard-head-to-head': 'Napraw head to head',
|
||||
'standard-head-to-head-tooltip':
|
||||
'Naprawia nieskończoną synchronizację podczas próby połączenia w trybie head to head',
|
||||
'standard-bypass-1080p': 'Obejdź sprawdzenie 1080p',
|
||||
'standard-bypass-120hz': 'Obejdź sprawdzenie 120Hz',
|
||||
'standard-force-free-play-text': 'Wymuś tekst kredytu FREE PLAY',
|
||||
'standard-force-free-play-text-tooltip':
|
||||
'Zastępuje liczbę kredytów tekstem FREE PLAY',
|
||||
'standard-custom-free-play-length': 'Długość tekstu FREE PLAY',
|
||||
'standard-custom-free-play-length-tooltip':
|
||||
'Zmienia długość tekstu wyświetlanego, gdy włączony jest wymuszony tekst kredytu FREE PLAY',
|
||||
'standard-custom-free-play-text': 'Customowy tekst FREE PLAY',
|
||||
'standard-custom-free-play-text-tooltip': 'Zastąp tekst FREE PLAY',
|
||||
'standard-localhost':
|
||||
'Zezwól na serwer pod adresem 127.0.0.1/localhost',
|
||||
'standard-credit-freeze': 'Zamroź kredyty',
|
||||
'standard-credit-freeze-tooltip':
|
||||
'Zapobiega używaniu kredytów. Co najmniej jeden kredyt musi być dostępny, aby rozpocząć grę lub zakupić bilety premium.',
|
||||
'standard-openssl-fix': 'Napraw błąd OpenSSL SHA',
|
||||
'standard-openssl-fix-tooltip':
|
||||
'Naprawia crash na procesorach Intel 10. generacji i nowszych',
|
||||
},
|
||||
cfg: {
|
||||
afterRestart: 'Wymaga restartu.',
|
||||
hardware: 'Prawdziwy czytnik',
|
||||
segatools: {
|
||||
general: 'Ogólne',
|
||||
builtIn: 'Wbudowany emulator',
|
||||
targetTooltip:
|
||||
'STARTLINER oczekuje czystych danych, pomijając odpakowane exe.',
|
||||
hooks: 'Hooki',
|
||||
ioModules: 'Moduły IO',
|
||||
ioModulesDesc:
|
||||
'Powinien odpowiadać twojej preferowanej metodzie wejścia.',
|
||||
ioBuiltIn: 'Wbudowany emulator (klawiatura)',
|
||||
io4: 'Natywne IO4',
|
||||
installTooltip: '{thing} można pobrać z pobierajki pakietów.',
|
||||
},
|
||||
display: {
|
||||
title: 'Ekran',
|
||||
resolution: 'Rozdzielczość',
|
||||
primary: 'Główny',
|
||||
target: 'Docelowy wyświetlacz',
|
||||
mode: 'Tryb',
|
||||
rotation: 'Obrót',
|
||||
refreshRate: 'Częstotliwość odświeżania',
|
||||
borderlessFullscreen: 'Bezramkowy tryb pełnoekranowy',
|
||||
borderlessFullscreenTooltip:
|
||||
'Dopasuj rozdzielczość wyświetlacza do gry.',
|
||||
dontSwitchPrimary: 'Pomiń przełączanie głównego wyświetlacza',
|
||||
dontSwitchPrimaryTooltip:
|
||||
'Włącz tę opcję tylko wtedy, gdy przełączanie głównego wyświetlacza powoduje problemy. Monitory muszą mieć dopasowaną częstotliwość odświeżania.',
|
||||
index: 'Indeks wyświetlacza',
|
||||
portrait: 'Pion',
|
||||
landscape: 'Poziom',
|
||||
flipped: 'Odwrócony',
|
||||
window: 'Okno',
|
||||
borderless: 'Okno bez ramki',
|
||||
fullscreen: 'Pełny ekran',
|
||||
},
|
||||
network: {
|
||||
title: 'Sieć',
|
||||
type: 'Typ sieci',
|
||||
remote: 'Zdalny',
|
||||
localArtemis: 'Lokalny (ARTEMiS)',
|
||||
artemisPath: 'Lokalizacja ARTEMiSa',
|
||||
address: 'Adres serwera',
|
||||
keychip: 'Keychip',
|
||||
subnet: 'Podsieć',
|
||||
addrSuffix: 'Sufiks adresu',
|
||||
},
|
||||
aime: {
|
||||
type: 'Typ Aime',
|
||||
modules: 'Moduły Aime',
|
||||
code: 'Kod Aime',
|
||||
codeTooltip:
|
||||
'Dotyczy tylko wbudowanej emulacji lub zgodnych pakietów',
|
||||
aimedb: 'Użyj AiMeDB dla kart fizycznych',
|
||||
aimedbTooltip:
|
||||
'Decyduje czy karty fizyczne powinny używać AiMeDB do pobierania kodów dostępu. Jeśli łączysz się z hostowaną siecią, włącz tę opcję, aby załadować te same dane konta, jakie uzyskałxbyś na fizycznym cabie.',
|
||||
serialPort: 'Port szeregowy Aime',
|
||||
serialPortTooltip: `Porty można sprawdzić w Urządzeniach i drukarkach lub na googlechromelabs.github.io/serial-terminal
|
||||
Dla AIC Pico powinien być wybrany port AIME.`,
|
||||
serverName: 'Nazwa serwera',
|
||||
},
|
||||
misc: {
|
||||
title: 'Różne',
|
||||
intel: 'Obejście buga OpenSSL dla procesorów Intel ≥10 generacji',
|
||||
intelTooltip: 'Zaleca się zamiast tego załatać amdaemon.',
|
||||
other: 'Inne opcje segatools',
|
||||
otherTooltip:
|
||||
'Zaawansowane lub sytuacyjne opcje, które nie są objęte przez STARTLINERA',
|
||||
},
|
||||
extensions: {
|
||||
title: 'Rozszerzenia',
|
||||
bepInExConsole: 'Konsola BepInExa',
|
||||
audioMode: 'Tryb audio',
|
||||
audioTooltip:
|
||||
'Tryb ekskluzywny 2-kanałowy wymaga 7EVENDAYSHOLIDAYS-ExclusiveAudio',
|
||||
audioShared: 'Współdzielony',
|
||||
audio6Ch: 'Ekskluzywny 6-kanałowy',
|
||||
audio2Ch: 'Ekskluzywny 2-kanałowy',
|
||||
sampleRate: 'Częstotliwość',
|
||||
blacklist: 'Czarna lista utworów',
|
||||
blacklistTooltip:
|
||||
'Utwory w tym zakresie ID nie będą zapisywane ani przesyłane',
|
||||
bonusTracks: 'Odblokuj Bonusowe Utwory',
|
||||
bonusTracksTooltip:
|
||||
'Wyłączenie tej opcji może pomóc w uporządkowaniu listy utworów',
|
||||
saekawa: 'Plik konfiguracyjny Saekawy',
|
||||
inohara: 'Plik konfiguracyjny Inohary',
|
||||
},
|
||||
keyboard: {
|
||||
title: 'Klawiatura',
|
||||
tooltip:
|
||||
'Dotyczy tylko wtedy, gdy moduł IO jest ustawiony na wbudowaną emulację lub zgodny moduł (np. mu3io.NET)',
|
||||
leverMode: 'Tryb wajchy',
|
||||
mouse: 'Mysz',
|
||||
irTooltip:
|
||||
'Jeśli grasz na klawiaturze, ustaw tylko ir1; pozostałe zostaw wyłączone',
|
||||
},
|
||||
wine: {
|
||||
prefix: 'Wine prefix',
|
||||
runtime: 'Lokalizacja Wine',
|
||||
},
|
||||
startliner: {
|
||||
offlineMode: 'Tryb offline',
|
||||
offlineModeTooltip: 'Wyłącza pobierajkę pakietów.',
|
||||
autoUpdate: 'Automatyczne aktualizacje',
|
||||
verbose: 'Szczegółowe logi',
|
||||
},
|
||||
},
|
||||
onboarding: {
|
||||
or: 'lub',
|
||||
backButton: 'przycisku z tyłu',
|
||||
standard: `
|
||||
Możesz utknąć na następującym ekranie:
|
||||
|
||||
{bigblack}Aグループの基準機から設定を取得{endbig}
|
||||
|
||||
Wówczas musisz przejść do menu testowego i w ustawieniach gry {black}ゲーム設定{end} przełączyć z "podążaj za standardem" {black}基準機に従う{end} na "standard" {black}基準機{end}.
|
||||
|
||||
Do menu testowego możesz dostać się za pomocą %TESTMENU%.
|
||||
`,
|
||||
|
||||
'ongeki-system-processing': `
|
||||
Możesz utknąć na tym ekranie przez kilka(naście) minut. _To jest normalne_. Gra po prostu potrzebuje dużo czasu na załadowanie danych.
|
||||
|
||||
Jeśli zainstalujesz <code>7EVENDAYSHOLIDAYS/LoadBoost</code>, kolejne uruchomienia będą znacznie szybsze.
|
||||
`,
|
||||
|
||||
'ongeki-lever': `
|
||||
Musisz również skalibrować wajchę; w przeciwnym razie możesz otrzymać błąd 3301.
|
||||
|
||||
Przejdź do ustawień wajchy ({black}レバー設定{end}), przesuń wajchę do obu krawędzi, a następnie naciśnij "koniec" ({black}終了{end}) i "zapisz" ({black}保存する{end}).
|
||||
`,
|
||||
|
||||
'chunithm-server': `
|
||||
Jeśli utkniesz na tym ekranie, zrestartuj grę.
|
||||
|
||||
Jeśli problem będzie się powtarzał, {link}sprawdź swoją konfigurację sieciową{endlink}.
|
||||
`,
|
||||
|
||||
finale: `
|
||||
Możesz uzyskać dostęp do tej strony w każdej chwili, klikając prawym przyciskiem myszy przycisk START.
|
||||
|
||||
Dodatkowe zasoby:
|
||||
|
||||
- {segaguide}SEGAguide{endlink}
|
||||
- {twotorial}two-torial{endlink}
|
||||
|
||||
## Miłej zabawy
|
||||
`,
|
||||
},
|
||||
};
|
@ -6,14 +6,15 @@ import PrimeVue from 'primevue/config';
|
||||
import ConfirmationService from 'primevue/confirmationservice';
|
||||
import Tooltip from 'primevue/tooltip';
|
||||
import App from './components/App.vue';
|
||||
import { i18n } from './i18n';
|
||||
import { changePrimaryColor } from './util';
|
||||
|
||||
const pinia = createPinia();
|
||||
const app = createApp(App);
|
||||
|
||||
const Preset = definePreset(Theme, {});
|
||||
|
||||
app.use(pinia);
|
||||
app.use(i18n);
|
||||
app.use(PrimeVue, {
|
||||
theme: {
|
||||
preset: Preset,
|
||||
|
@ -4,6 +4,7 @@ import { listen } from '@tauri-apps/api/event';
|
||||
import * as path from '@tauri-apps/api/path';
|
||||
import { PhysicalSize, getCurrentWindow } from '@tauri-apps/api/window';
|
||||
import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs';
|
||||
import { Locale, setLocale as actualSetLocale } from './i18n';
|
||||
import { invoke, invoke_nopopup } from './invoke';
|
||||
import { Dirs, Feature, Game, Package, Profile, ProfileMeta } from './types';
|
||||
import {
|
||||
@ -31,23 +32,24 @@ export const useGeneralStore = defineStore('general', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const configDir = computed(() => {
|
||||
const loadDirs = async () => {
|
||||
if (dirs.value === null) {
|
||||
throw new Error('Invalid directory access');
|
||||
const d = (await invoke('list_directories')) as Dirs;
|
||||
dirs.value = d;
|
||||
}
|
||||
return dirs.value.config_dir;
|
||||
};
|
||||
|
||||
const configDir = computed(async () => {
|
||||
await loadDirs();
|
||||
return dirs.value!.config_dir;
|
||||
});
|
||||
const dataDir = computed(() => {
|
||||
if (dirs.value === null) {
|
||||
throw new Error('Invalid directory access');
|
||||
}
|
||||
return dirs.value.data_dir;
|
||||
const dataDir = computed(async () => {
|
||||
await loadDirs();
|
||||
return dirs.value!.data_dir;
|
||||
});
|
||||
const cacheDir = computed(() => {
|
||||
if (dirs.value === null) {
|
||||
throw new Error('Invalid directory access');
|
||||
}
|
||||
return dirs.value.cache_dir;
|
||||
const cacheDir = computed(async () => {
|
||||
await loadDirs();
|
||||
return dirs.value!.cache_dir;
|
||||
});
|
||||
|
||||
return {
|
||||
@ -320,8 +322,8 @@ export const usePrfStore = defineStore('prf', () => {
|
||||
const generalStore = useGeneralStore();
|
||||
|
||||
const configDir = computed(async () => {
|
||||
return await path.join(
|
||||
generalStore.configDir,
|
||||
return path.join(
|
||||
await generalStore.configDir,
|
||||
`profile-${current.value?.meta.game}-${current.value?.meta.name}`
|
||||
);
|
||||
});
|
||||
@ -371,6 +373,11 @@ export const useClientStore = defineStore('client', () => {
|
||||
const verbose = ref(false);
|
||||
const theme: Ref<'light' | 'dark' | 'system'> = ref('system');
|
||||
const onboarded: Ref<Game[]> = ref([]);
|
||||
const locale: Ref<Locale> = ref('en');
|
||||
const currentTab: Ref<string> = ref('users');
|
||||
const pkgListMode: Ref<'namespace' | 'type' | 'category'> =
|
||||
ref('namespace');
|
||||
const hiddenCategories: Ref<string[]> = ref([]);
|
||||
|
||||
const _scaleValue = (value: ScaleType) =>
|
||||
value === 's' ? 1 : value === 'm' ? 1.25 : value === 'l' ? 1.5 : 2;
|
||||
@ -410,7 +417,7 @@ export const useClientStore = defineStore('client', () => {
|
||||
const input = JSON.parse(
|
||||
await readTextFile(
|
||||
await path.join(
|
||||
generalStore.configDir,
|
||||
await generalStore.configDir,
|
||||
'client-options.json'
|
||||
)
|
||||
)
|
||||
@ -433,6 +440,23 @@ export const useClientStore = defineStore('client', () => {
|
||||
if (input.onboarded) {
|
||||
onboarded.value = input.onboarded;
|
||||
}
|
||||
|
||||
if (input.locale) {
|
||||
locale.value = input.locale;
|
||||
}
|
||||
|
||||
if (input.currentTab) {
|
||||
currentTab.value = input.currentTab;
|
||||
}
|
||||
|
||||
if (input.pkgListMode) {
|
||||
pkgListMode.value = input.pkgListMode;
|
||||
}
|
||||
|
||||
if (input.hiddenCategories) {
|
||||
hiddenCategories.value = input.hiddenCategories;
|
||||
}
|
||||
await setLocale(locale.value);
|
||||
await setTheme(theme.value);
|
||||
} catch (e) {
|
||||
console.error(`Error reading client options: ${e}`);
|
||||
@ -457,7 +481,10 @@ export const useClientStore = defineStore('client', () => {
|
||||
const size = await w.innerSize();
|
||||
|
||||
await writeTextFile(
|
||||
await path.join(generalStore.configDir, 'client-options.json'),
|
||||
await path.join(
|
||||
await generalStore.configDir,
|
||||
'client-options.json'
|
||||
),
|
||||
JSON.stringify({
|
||||
scaleFactor: scaleFactor.value,
|
||||
windowSize: {
|
||||
@ -466,6 +493,10 @@ export const useClientStore = defineStore('client', () => {
|
||||
},
|
||||
theme: theme.value,
|
||||
onboarded: onboarded.value,
|
||||
locale: locale.value,
|
||||
currentTab: currentTab.value,
|
||||
pkgListMode: pkgListMode.value,
|
||||
hiddenCategories: hiddenCategories.value,
|
||||
})
|
||||
);
|
||||
};
|
||||
@ -518,6 +549,12 @@ export const useClientStore = defineStore('client', () => {
|
||||
await save();
|
||||
};
|
||||
|
||||
const setLocale = async (loc: Locale) => {
|
||||
locale.value = loc;
|
||||
await save();
|
||||
await actualSetLocale(loc);
|
||||
};
|
||||
|
||||
getCurrentWindow().onResized(async ({ payload }) => {
|
||||
// For whatever reason this is 0 when minimized
|
||||
if (payload.width > 0) {
|
||||
@ -532,8 +569,12 @@ export const useClientStore = defineStore('client', () => {
|
||||
verbose,
|
||||
theme,
|
||||
onboarded,
|
||||
locale,
|
||||
timeout,
|
||||
scaleModel,
|
||||
currentTab,
|
||||
pkgListMode,
|
||||
hiddenCategories,
|
||||
_scaleValue,
|
||||
scaleValue,
|
||||
load,
|
||||
@ -544,5 +585,6 @@ export const useClientStore = defineStore('client', () => {
|
||||
setVerbose,
|
||||
setTheme,
|
||||
setOnboarded,
|
||||
setLocale,
|
||||
};
|
||||
});
|
||||
|
13
src/types.ts
13
src/types.ts
@ -56,6 +56,7 @@ export interface ProfileData {
|
||||
display: DisplayConfig;
|
||||
network: NetworkConfig;
|
||||
bepinex: BepInExConfig;
|
||||
wine: WineConfig;
|
||||
mu3_ini: Mu3IniConfig | undefined;
|
||||
keyboard: KeyboardConfig | undefined;
|
||||
patches: {
|
||||
@ -105,9 +106,17 @@ export interface BepInExConfig {
|
||||
console: boolean;
|
||||
}
|
||||
|
||||
export interface WineConfig {
|
||||
runtime: string;
|
||||
prefix: string;
|
||||
}
|
||||
|
||||
export interface Mu3IniConfig {
|
||||
audio?: 'Shared' | 'Excl6Ch' | 'Excl2Ch';
|
||||
// blacklist?: [number, number];
|
||||
sample_rate: number;
|
||||
blacklist?: [number, number];
|
||||
gp: number;
|
||||
enable_bonus_tracks: boolean;
|
||||
}
|
||||
|
||||
export interface OngekiButtons {
|
||||
@ -164,7 +173,7 @@ export interface Patch {
|
||||
id: string;
|
||||
name: string;
|
||||
tooltip: string;
|
||||
type: undefined | 'number';
|
||||
type: undefined | 'number' | 'hex';
|
||||
default: number;
|
||||
min: number;
|
||||
max: number;
|
||||
|
@ -3,11 +3,7 @@ import { Feature, Game, Package } from './types';
|
||||
|
||||
export const changePrimaryColor = (game: Game | null) => {
|
||||
const color =
|
||||
game === 'ongeki'
|
||||
? 'pink'
|
||||
: game === 'chunithm'
|
||||
? 'yellow'
|
||||
: 'bluegray';
|
||||
game === 'ongeki' ? 'pink' : game === 'chunithm' ? 'yellow' : 'purple';
|
||||
|
||||
updatePrimaryPalette({
|
||||
50: `{${color}.50}`,
|
||||
|
Reference in New Issue
Block a user