Compare commits

..

52 Commits

Author SHA1 Message Date
SpikeHD
c735b8936a Merge pull request #151 from Grasscutters/more_cli
Passable amount of CLI options
2023-04-22 16:16:56 -07:00
SpikeHD
c88e3ce3a7 Merge pull request #152 from NotThorny/more_cli-2
Arg fixes
2023-04-22 16:15:12 -07:00
Thoronium
e5d151f512 Formatting
For rustfmt
2023-04-22 17:08:39 -06:00
Thoronium
361737c00d Unpatch when exiting disabled GUI 2023-04-22 16:56:59 -06:00
Thoronium
0f08bb5fbf Update comment 2023-04-22 16:10:07 -06:00
Thoronium
df674abd94 Format 2023-04-22 16:06:04 -06:00
Thoronium
c21c5ac0b1 Fix launch server from args 2023-04-22 16:05:22 -06:00
Thoronium
6ac0182784 Fix args 2023-04-22 15:41:54 -06:00
SpikeHD
2fa203163d game args err check 2023-04-21 18:25:23 -07:00
SpikeHD
75c6481778 another typo 2023-04-21 18:13:49 -07:00
SpikeHD
12b60d3b2a typo oops 2023-04-21 18:13:12 -07:00
SpikeHD
88b5b40300 lint and format 2023-04-21 18:10:05 -07:00
SpikeHD
45ecfcbeb3 fix host option 2023-04-21 18:09:27 -07:00
SpikeHD
e6492825dc optionally patch/unpatch 2023-04-21 18:06:53 -07:00
SpikeHD
3141bcea41 game options 2023-04-21 17:46:31 -07:00
SpikeHD
64a04e927c set host 2023-04-21 17:37:21 -07:00
SpikeHD
4bcfd7ec09 more options 2023-04-21 17:03:04 -07:00
SpikeHD
a67eca49e5 real arg parse 2023-04-21 16:50:53 -07:00
SpikeHD
48dff50a5d config reading 2023-04-21 16:28:56 -07:00
SpikeHD
5480af7835 quick fmt 2023-04-21 12:49:09 -07:00
SpikeHD
829b9822cb re-add opening as admin 2023-04-21 12:47:44 -07:00
SpikeHD
a6716e80f4 new release notifications 2023-04-21 12:47:17 -07:00
SpikeHD
6962518ced Merge pull request #148 from NotThorny/NonElevated
Non-elevated option & bugfix
2023-04-14 22:48:28 -07:00
Thoronium
09d0d8287f Run prettier
I forgor before
2023-04-14 23:36:25 -06:00
Thoronium
7bfa5e8e11 Add non-elevated game option 2023-04-14 23:30:54 -06:00
Thoronium
5de80f1655 Auto-remove spaces from address 2023-04-14 23:30:54 -06:00
Thoronium
33ce547bb2 Remove unnecessary duplicate patch 2023-04-14 23:30:54 -06:00
Thoronium
864f9f199c Fix mods appearing suck on extraction 2023-04-14 23:30:54 -06:00
Thoronium
5d8e4d7311 Fix download buttons getting stuck 2023-04-14 23:30:54 -06:00
SpikeHD
9f0567da6a Merge pull request #146 from NotThorny/MoreFixes
More fixes
2023-04-09 12:20:30 -07:00
SpikeHD
56453ff55b Merge pull request #147 from thecutefinder/patch-2
GNU-ify
2023-04-09 12:20:12 -07:00
Colleiflower
61c5cc8d57 GNU-ify
Maintains consistency with other Grasscutter projects, e.g. https://github.com/Grasscutters/Grasscutter/pull/2080
2023-04-09 07:06:30 +00:00
SpikeHD
7bbc7e3c10 Update README.md 2023-04-08 09:46:15 -07:00
SpikeHD
5b7c1307d9 Update README.md 2023-04-08 09:40:17 -07:00
Thoronium
096992572c Add 7z support 2023-04-07 13:12:41 -06:00
Thoronium
68f0cce154 Run prettier 2023-04-07 11:23:35 -06:00
Thoronium
19db69646f Remove whitespace 2023-04-07 10:45:05 -06:00
Thoronium
82f40186fe Translations for new help text 2023-04-07 10:45:05 -06:00
Thoronium
d7b2aa25cc Add mods download 2023-04-07 10:32:53 -06:00
Thoronium
3356bddb42 Fix horny blur on installed mods 2023-04-07 10:32:53 -06:00
Thoronium
db11cf7907 Change encryption to checkbox 2023-04-07 10:32:53 -06:00
Thoronium
891dbb41aa Add delay button for 3dmigoto 2023-04-07 10:32:53 -06:00
Thoronium
cf6ec3da82 Test permission change for setting proxy 2023-04-07 10:32:41 -06:00
SpikeHD
b2d6f390fb Merge pull request #144 from NotThorny/IssuesFixes
Bugfixes
2023-04-06 11:42:09 -07:00
Thoronium
a920d70e53 Run prettier 2023-04-05 22:06:54 -06:00
nana
d2e7759eec Create pt-br.json
Adds a brazillian portuguese translation file.
2023-04-05 22:05:21 -06:00
Thoronium
77380e357c Show wrench instantly on setting 3dmigoto 2023-04-05 22:02:50 -06:00
Thoronium
33ddc36741 Only intercept game traffic
Bump version
2023-04-05 22:02:38 -06:00
SpikeHD
bbd18d33e9 Merge pull request #143 from ariqpradipa/src-tauri/src/lang
Refactor path concatenation and make error handling src-tauri/src/lang.rs
2023-04-04 08:29:21 -07:00
Ariq Pradipa Santoso
b66f01232b revert: lang path 2023-04-04 12:16:33 +07:00
Ariq Pradipa Santoso
b6a2e4dbc0 fix: improve error handling in get_languages() 2023-04-04 01:09:54 +07:00
Ariq Pradipa Santoso
45ee8fbb23 refactor: optimze path concatenation in get_lang() 2023-04-04 01:08:34 +07:00
41 changed files with 1139 additions and 186 deletions

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"rust-analyzer.linkedProjects": [".\\src-tauri\\Cargo.toml"]
}

View File

@@ -9,6 +9,7 @@ A game launcher designed to easily proxy traffic from anime game to private serv
- [Client Patching Notice](#client-patching-notice)
- [Download](#download)
- [Setup](#setup)
- [Troubleshooting](#troubleshooting)
- [Developer Quick-start](#developer-quickstart)
- [Setup](#setup)
- [Building](#building)
@@ -37,7 +38,7 @@ Download and open the MSI, and once installed, run Cultivation as administrator.
- Download Cultivation
- If you are on Windows 10 or 11, use the MSI
- If you are on Windows 7, or the MSI doesn't work, use the zip and download [WebView](https://developer.microsoft.com/en-us/microsoft-edge/webview2/)
- If you are on Linux or MacOS, [help us port Windows-specific system calls to Linux/MacOS!](https://github.com/Grasscutters/Cultivation/issues/7)
- If you are on GNU/Linux or MacOS, [help us port Windows-specific system calls to GNU/Linux and MacOS!](https://github.com/Grasscutters/Cultivation/issues/7)
- Install or extract Cultivation
- Open Cultivation **_as administrator_**
- Before clicking randomly on stuff, in options (top right cog icon), set your Game Install Path.
@@ -57,6 +58,19 @@ Download and open the MSI, and once installed, run Cultivation as administrator.
- Any specific Cultivation issues should go in [the issues section](/issues)
- Any Grasscutter server related issues should go in [the Grasscutter issues section](https://github.com/Grasscutters/Grasscutter)
# Troubleshooting
### White screen, insta-crash or something similar
- First try [running in Windows 8 compatibility mode](https://www.lifewire.com/run-older-programs-with-windows-10-compatibility-mode-4587064).
- If that doesn't work, fully uninstall and reinstall [WebView2](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section).
- If you are having trouble uninstalling it, try deleting this registry folder and uninstalling again `Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}`
- You can also try [uninstalling from the Command Prompt](https://superuser.com/a/1743626)
### Internet not working after use
Please allow the Cultivation window to pop back up once you have quit out of the game. This tells you that it knows you closed the game, and that it has reverted your proxy settings. If you have closed Cultivation before this happens, or have had some other issue with your internet, go to your [proxy settings in Windows](https://techviral.net/check-proxy-server-settings-in-windows/) and disable the "Manual proxy setup".
# Developer Quickstart
### Setup

View File

@@ -1,6 +1,6 @@
{
"name": "cultivation",
"version": "1.0.23",
"version": "1.0.26",
"private": true,
"dependencies": {
"@tauri-apps/api": "^1.0.0-rc.5",

245
src-tauri/Cargo.lock generated
View File

@@ -76,6 +76,16 @@ version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
[[package]]
name = "args"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4b7432c65177b8d5c032d56e020dd8d407e939468479fc8c300e2d93e6d970b"
dependencies = [
"getopts",
"log",
]
[[package]]
name = "asn1-rs"
version = "0.3.1"
@@ -258,6 +268,21 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b"
[[package]]
name = "bit-set"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
dependencies = [
"bit-vec",
]
[[package]]
name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -270,6 +295,15 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"generic-array",
]
[[package]]
name = "block-buffer"
version = "0.10.2"
@@ -620,6 +654,21 @@ dependencies = [
"libc",
]
[[package]]
name = "crc"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484"
[[package]]
name = "crc32fast"
version = "1.3.2"
@@ -741,11 +790,13 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
name = "cultivation"
version = "0.1.0"
dependencies = [
"args",
"cc",
"ctrlc",
"duct",
"file_diff",
"futures-util",
"getopts",
"http",
"hudsucker",
"is_elevated",
@@ -759,6 +810,7 @@ dependencies = [
"rustls-pemfile",
"serde",
"serde_json",
"sevenz-rust",
"sudo",
"sysinfo",
"tauri",
@@ -885,13 +937,22 @@ dependencies = [
"syn",
]
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array",
]
[[package]]
name = "digest"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"block-buffer",
"block-buffer 0.10.2",
"crypto-common",
"subtle",
]
@@ -1052,14 +1113,25 @@ checksum = "31a7a908b8f32538a2143e59a6e4e2508988832d5d4d6f7c156b3cbc762643a5"
[[package]]
name = "filetime"
version = "0.2.17"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c"
checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"windows-sys 0.36.1",
"windows-sys 0.48.0",
]
[[package]]
name = "filetime_creation"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d961767622336521cc48b3de810fce4edbf02d0c21079d78f3a6eeaf45b9450"
dependencies = [
"cfg-if",
"filetime",
"windows-sys 0.48.0",
]
[[package]]
@@ -1327,6 +1399,15 @@ dependencies = [
"version_check",
]
[[package]]
name = "getopts"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
"unicode-width",
]
[[package]]
name = "getrandom"
version = "0.1.16"
@@ -1567,7 +1648,7 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
"digest 0.10.6",
]
[[package]]
@@ -1907,9 +1988,9 @@ dependencies = [
[[package]]
name = "js-sys"
version = "0.3.58"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
dependencies = [
"wasm-bindgen",
]
@@ -2001,6 +2082,15 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "lzma-rust"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808dc37ccba979c213304880eadaab444bb522a5fe79acca9e90ec62377125c2"
dependencies = [
"byteorder",
]
[[package]]
name = "mac"
version = "0.1.1"
@@ -2663,10 +2753,10 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271779f35b581956db91a3e55737327a03aa051e90b1c47aeb189508533adfd7"
dependencies = [
"digest",
"digest 0.10.6",
"hmac",
"password-hash",
"sha2",
"sha2 0.10.2",
]
[[package]]
@@ -3619,6 +3709,22 @@ dependencies = [
"stable_deref_trait",
]
[[package]]
name = "sevenz-rust"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12eea80650ae08d8fe6f657d6249d757298bc7a241e00d190ac57cef0e74e02"
dependencies = [
"bit-set",
"byteorder",
"crc",
"filetime_creation",
"js-sys",
"lzma-rust",
"sha2 0.9.9",
"wasm-bindgen",
]
[[package]]
name = "sha-1"
version = "0.10.1"
@@ -3627,7 +3733,7 @@ checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
"digest 0.10.6",
]
[[package]]
@@ -3638,7 +3744,20 @@ checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
"digest 0.10.6",
]
[[package]]
name = "sha2"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
dependencies = [
"block-buffer 0.9.0",
"cfg-if",
"cpufeatures",
"digest 0.9.0",
"opaque-debug",
]
[[package]]
@@ -3649,7 +3768,7 @@ checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
"digest 0.10.6",
]
[[package]]
@@ -4065,7 +4184,7 @@ dependencies = [
"semver 1.0.12",
"serde",
"serde_json",
"sha2",
"sha2 0.10.2",
"tauri-utils",
"thiserror",
"time 0.3.11",
@@ -4526,6 +4645,12 @@ version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "unicode-xid"
version = "0.2.3"
@@ -4703,9 +4828,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.81"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -4713,13 +4838,13 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.81"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
@@ -4740,9 +4865,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.81"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -4750,9 +4875,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.81"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
@@ -4763,9 +4888,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.81"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
[[package]]
name = "web-sys"
@@ -5027,7 +5152,16 @@ version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
"windows-targets 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.0",
]
[[package]]
@@ -5036,15 +5170,30 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm 0.48.0",
"windows_aarch64_msvc 0.48.0",
"windows_i686_gnu 0.48.0",
"windows_i686_msvc 0.48.0",
"windows_x86_64_gnu 0.48.0",
"windows_x86_64_gnullvm 0.48.0",
"windows_x86_64_msvc 0.48.0",
]
[[package]]
name = "windows-tokens"
version = "0.37.0"
@@ -5057,6 +5206,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.32.0"
@@ -5081,6 +5236,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.24.0"
@@ -5111,6 +5272,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.24.0"
@@ -5141,6 +5308,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.24.0"
@@ -5171,12 +5344,24 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.24.0"
@@ -5207,6 +5392,12 @@ version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winreg"
version = "0.10.1"

View File

@@ -26,6 +26,10 @@ sudo = "0.6.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.0.7", features = ["api-all"] }
# Arg parsing
args = "2.0"
getopts = "0.2"
# Access system process info.
sysinfo = "0.24.6"
@@ -33,6 +37,7 @@ sysinfo = "0.24.6"
zip-extract = "0.1.1"
unrar = "0.4.4"
zip = "0.6.2"
sevenz-rust = "0.2.9"
# For creating a "global" downloads list.
once_cell = "1.13.0"

View File

@@ -29,7 +29,8 @@
"use_proxy": "使用内置代理",
"wipe_login": "清除登录缓存",
"horny_mode": "Horny 模式",
"auto_mongodb": "自动启动 MongoDB"
"auto_mongodb": "自动启动 MongoDB",
"un_elevated": "非提升运行游戏(无管理员)"
},
"downloads": {
"grasscutter_fullbuild": "下载 Grasscutter 一体化",
@@ -44,7 +45,9 @@
"resources": "下载 Grasscutter 资源",
"game": "下载游戏",
"aio_header": "多合一下载:",
"individual_header": "个别部分下载:"
"individual_header": "个别部分下载:",
"mods_header": "Mods:",
"migoto": "下载 GIMI 3dmigoto"
},
"download_status": {
"downloading": "下载中",
@@ -76,7 +79,9 @@
"resources": "资源文件在运行 Grasscutter 服务器时是必要的。此选项在已经存在资源文件时不可选。",
"emergency_rsa": "在出现意外情况时自动将 RSA 恢复到原始版本",
"use_proxy": "使用 Cultivation 的内置代理。除非你使用 Fiddler 等软件,否则应启用此项。",
"patch_rsa": "自动修改和恢复 RSA 补丁。 除非您玩的是旧版/非官方版本,或者您手动修改了 RSA否则应该启用此功能。"
"patch_rsa": "自动修改和恢复 RSA 补丁。 除非您玩的是旧版/非官方版本,或者您手动修改了 RSA否则应该启用此功能。",
"add_delay": "在 3dmigoto 加载程序中设置延迟! \n这应该可以解决加载问题但会在启动游戏时加载 3dmigoto 时增加一点延迟。 \n您现在可以再次使用 3dmigoto 启动。",
"migoto": "用于从 GameBanana 导入模型"
},
"swag": {
"akebi_name": "Akebi",

View File

@@ -29,7 +29,8 @@
"use_proxy": "使用內建代理伺服器",
"wipe_login": "擦除登錄緩存",
"horny_mode": "Horny模式",
"auto_mongodb": "自動啟動 MongoDB"
"auto_mongodb": "自動啟動 MongoDB",
"un_elevated": "在不升高的情况下运行游戏(没有管理员)。"
},
"downloads": {
"grasscutter_fullbuild": "下載Grasscutter多合一下載",
@@ -44,7 +45,9 @@
"resources": "下載Grasscutter資源Resources",
"game": "下載遊戲",
"aio_header": "多合一下載:",
"individual_header": "個別部分下載:"
"individual_header": "個別部分下載:",
"mods_header": "Mods:",
"migoto": "下載GIMI 3dmigoto"
},
"download_status": {
"downloading": "下載中",
@@ -76,7 +79,9 @@
"resources": "資源文件在架設一個Grasscutter伺服器時是必要的。 這個選項會在您已經有裡面有檔案的資源資料夾時不可選。",
"emergency_rsa": "一旦有東西出了問題此選項可以把您的rsa恢復成官方版本。",
"use_proxy": "使用Cultivation內建的代理伺服器。此選項應該被啟用除非你使用其他的代理伺服器。",
"patch_rsa": "自動修補和恢復RSA。除非您的遊戲版本是舊的或者是非官方的此選項應該被啟用。"
"patch_rsa": "自動修補和恢復RSA。除非您的遊戲版本是舊的或者是非官方的此選項應該被啟用。",
"add_delay": "在 3dmigoto 加載程序中設置延遲! \n這應該可以解決加載問題但會在啟動遊戲時加載 3dmigoto 時增加一點延遲。 \n您現在可以再次使用 3dmigoto 啟動。",
"migoto": "用於從 GameBanana 導入模型"
},
"swag": {
"akebi_name": "Akebi",

View File

@@ -28,7 +28,8 @@
"use_proxy": "Gebruik interne proxy",
"wipe_login": "Wis de inlogcache",
"horny_mode": "Geile modus",
"auto_mongodb": "Start automatisch MongoDB"
"auto_mongodb": "Start automatisch MongoDB",
"un_elevated": "Führen Sie das Spiel nicht erhöht aus (kein Admin)"
},
"downloads": {
"grasscutter_fullbuild": "Alles in Einem Grasscutter Daten herunterladen",
@@ -43,7 +44,9 @@
"resources": "Grasscutter Ressourcen herunterladen",
"game": "Spiel herunterladen",
"aio_header": "Alles in Einem herunterladen",
"individual_header": "Einzelne Teile herunterladen:"
"individual_header": "Einzelne Teile herunterladen:",
"mods_header": "Mods:",
"migoto": "GIMI 3dmigoto herunterladen"
},
"download_status": {
"downloading": "Lädt herunter",
@@ -74,7 +77,9 @@
"resources": "Diese werden auch benötigt, um einen Grasscutter-Server auszuführen. Diese Schaltfläche ist grau, wenn Sie einen bestehenden Ressourcenordner mit Inhalten haben",
"emergency_rsa": "Im Fall, dass etwas schief laufen sollte, kannst du deine RSA auf die letzte offizielle Version zurücksetzen",
"use_proxy": "Nutze den internen Proxy von Cultivation. Du solltest dies aktivieren, es sei denn du nutzt Programme wie Fiddler",
"patch_rsa": "Patche und aktualisiere deine RSA automatisch. Solange du nicht mit einer alten/nicht offiziellen Version spielst oder deine RSA manuell gepatcht hast, sollte dies aktiviert sein."
"patch_rsa": "Patche und aktualisiere deine RSA automatisch. Solange du nicht mit einer alten/nicht offiziellen Version spielst oder deine RSA manuell gepatcht hast, sollte dies aktiviert sein.",
"add_delay": "Verzögerung im 3dmigoto-Lader einstellen! \nDies sollte die Ladeprobleme beheben, führt aber zu einer kleinen Verzögerung, wenn 3dmigoto beim Start des Spiels geladen wird. \nSie können nun wieder mit 3dmigoto starten.",
"migoto": "Zum Importieren von Modellen von GameBanana"
},
"swag": {
"akebi_name": "Akebi",

View File

@@ -29,7 +29,8 @@
"use_proxy": "Use Internal Proxy",
"wipe_login": "Wipe Login Cache",
"horny_mode": "Horny Mode",
"auto_mongodb": "Automatically Start MongoDB"
"auto_mongodb": "Automatically Start MongoDB",
"un_elevated": "Run the game non-elevated (no admin)"
},
"downloads": {
"grasscutter_fullbuild": "Download Grasscutter All-in-One",
@@ -44,7 +45,9 @@
"resources": "Download Grasscutter Resources",
"game": "Download Game",
"aio_header": "All-in-One Downloads:",
"individual_header": "Individual downloads:"
"individual_header": "Individual downloads:",
"mods_header": "Mods:",
"migoto": "Download GIMI 3dmigoto"
},
"download_status": {
"downloading": "Downloading",
@@ -76,7 +79,9 @@
"resources": "These are also required to run a Grasscutter server. This button will be grey if you have an existing resources folder with contents inside",
"emergency_rsa": "In case something went wrong, force delete RSA patch.",
"use_proxy": "Use the Cultivation internal proxy. You should have this enabled unless you use something like Fiddler",
"patch_rsa": "Patch and unpatch your game RSA automatically. Unless playing with old/non-official versions (3.0 and older), this should be enabled."
"patch_rsa": "Patch and unpatch your game RSA automatically. Unless playing with old/non-official versions (3.0 and older), this should be enabled.",
"add_delay": "Set delay in 3dmigoto loader! \nThis should fix loading issues, but will add a small delay to when 3dmigoto is loaded upon launching the game. \nYou can now launch with 3dmigoto again.",
"migoto": "For importing models from GameBanana"
},
"swag": {
"akebi_name": "Akebi",

View File

@@ -29,7 +29,8 @@
"use_proxy": "Usar proxy interno",
"wipe_login": "Borrar caché de inicio de sesión",
"horny_mode": "Modo cachondo",
"auto_mongodb": "Iniciar automáticamente MongoDB"
"auto_mongodb": "Iniciar automáticamente MongoDB",
"un_elevated": "Ejecutar el juego no elevado (no admin)"
},
"downloads": {
"grasscutter_fullbuild": "Descargar Datos todo en uno de Grasscutter",
@@ -44,7 +45,9 @@
"resources": "Descargar Recursos de Grasscutter",
"game": "Descarga el juego",
"aio_header": "Descargas todo en uno:",
"individual_header": "Descargas de piezas individuales:"
"individual_header": "Descargas de piezas individuales:",
"mods_header": "Mods:",
"migoto": "Descargar GIMI 3dmigoto"
},
"download_status": {
"downloading": "Descargando",
@@ -72,7 +75,9 @@
"gc_dev_jar": "Descargue la última versión de Desarrollo de Grasscutter, que incluye archivos jar y archivos de datos.",
"gc_stable_data": "Descargue los archivos de Datos Estables actuales de Grasscutter, que no vienen con un archivo jar. Esto es útil para actualizar.",
"gc_dev_data": "Descargue los últimos archivos de Datos de Desarrollo de Grasscutter, que no vienen con un archivo jar. Esto es útil para actualizar.",
"resources": "Estos también son necesarios para ejecutar un servidor Grasscutter. Este botón estará gris si tiene una carpeta de recursos existente con contenido dentro."
"resources": "Estos también son necesarios para ejecutar un servidor Grasscutter. Este botón estará gris si tiene una carpeta de recursos existente con contenido dentro.",
"add_delay": "¡Retraso en el cargador de 3dmigoto! \nEsto debería solucionar los problemas de carga, pero añadirá un pequeño retraso cuando 3dmigoto se cargue al iniciar el juego. \nAhora puede iniciar con 3dmigoto de nuevo.",
"migoto": "Para importar modelos de GameBanana"
},
"swag": {
"akebi": "Establecer el ejecutable de Akebi"

View File

@@ -29,7 +29,8 @@
"use_proxy": "Utiliser un proxy interne",
"wipe_login": "Effacer le cache de connexion",
"horny_mode": "Mode excitation",
"auto_mongodb": "Démarrer automatiquement MongoDB"
"auto_mongodb": "Démarrer automatiquement MongoDB",
"un_elevated": "Exécuter le jeu sans élévation (pas d'administrateur)"
},
"downloads": {
"grasscutter_fullbuild": "Telecharger Grasscutter tout-en-un",
@@ -43,7 +44,9 @@
"grasscutter_latest_update": "Mettre a jour Grasscutter (derniere version)",
"resources": "Telecharger les ressources logicielles de Grasscutter",
"aio_header": "Telechargements tout-en-un:",
"individual_header": "Telechargements de pièces individuelles:"
"individual_header": "Telechargements de pièces individuelles:",
"mods_header": "Mods:",
"migoto": "Telecharger GIMI 3dmigoto"
},
"download_status": {
"downloading": "Telechargement",
@@ -71,6 +74,8 @@
"gc_dev_jar": "Telecharger le dernier build en development de Grasscutter, ce qui inclut le fichier jar et les fichiers de donnees",
"gc_stable_data": "Telecharger le dernier build stable de Grasscutter, ce qui n'inclut pasle fichier jar. Cela est utile pour mettre a jour",
"gc_dev_data": "Telecharger le dernier build en development de Grasscutter, ce qui n'inclut pasle fichier jar. Cela est utile pour mettre a jour",
"resources": "Les ressources sont aussi necessaires pour lancer un serveur Grasscutter. Ce bouton deviendra gris si vous avez deja un fichier ressources avec les donnees dedans."
"resources": "Les ressources sont aussi necessaires pour lancer un serveur Grasscutter. Ce bouton deviendra gris si vous avez deja un fichier ressources avec les donnees dedans.",
"add_delay": "Définir un délai dans le chargeur de 3dmigoto ! \nCela devrait résoudre les problèmes de chargement, mais ajoutera un petit délai au chargement de 3dmigoto lors du lancement du jeu. \nVous pouvez maintenant lancer le jeu avec 3dmigoto.",
"migoto": "Pour importer des modèles depuis GameBanana"
}
}

View File

@@ -28,7 +28,8 @@
"use_proxy": "Gunakan Proxy Internal",
"wipe_login": "Menghapus Cache Login",
"horny_mode": "Mode Terangsang",
"auto_mongodb": "Mulai MongoDB secara otomatis"
"auto_mongodb": "Mulai MongoDB secara otomatis",
"un_elevated": "Jalankan game yang tidak ditinggikan (tanpa admin)"
},
"downloads": {
"grasscutter_fullbuild": "Sedang Mendownload Grasscutter Semua Dalam Satu",
@@ -42,7 +43,9 @@
"grasscutter_latest_update": "Sedang MengUpdate Grasscutter Terbaru",
"resources": "Mendownload Grasscutter Resources",
"aio_header": "Unduhan Semua Dalam Satu:",
"individual_header": "Unduhan Bagian Individual:"
"individual_header": "Unduhan Bagian Individual:",
"mods_header": "Mods:",
"migoto": "Sedang Mendownload GIMI 3dmigoto"
},
"download_status": {
"downloading": "Sedang Mendownload",
@@ -69,6 +72,8 @@
"gc_dev_jar": "Unduh Build Development Grasscutter saat ini, Dimana Ada Jar File Dan Data File.",
"gc_stable_data": "Unduh file data Grasscutter stabil saat ini, dimana Tidak Ada JAR file. Ini Berguna Untuk memperbarui.",
"gc_dev_data": "Unduh file data Grasscutter Development saat ini, dimana Tidak Ada JAR file. Ini Berguna Untuk memperbarui.",
"resources": "Ini juga diperlukan untuk menjalankan server Grasscutter. Tombol ini akan berwarna abu-abu jika Anda memiliki folder Resource yang ada dengan File di dalamnya"
"resources": "Ini juga diperlukan untuk menjalankan server Grasscutter. Tombol ini akan berwarna abu-abu jika Anda memiliki folder Resource yang ada dengan File di dalamnya",
"add_delay": "Atur penundaan di pemuat 3dmigoto! \nIni akan memperbaiki masalah pemuatan, tetapi akan menambah sedikit penundaan ketika 3dmigoto dimuat saat meluncurkan game. \nAnda sekarang dapat meluncurkan dengan 3dmigoto lagi.",
"migoto": "Untuk mengimpor model dari GameBanana"
}
}

View File

@@ -29,7 +29,8 @@
"use_proxy": "내부 프록시 사용",
"wipe_login": "로그인 캐시 지우기",
"horny_mode": "Horny 모드",
"auto_mongodb": "MongoDB 자동 시작"
"auto_mongodb": "MongoDB 자동 시작",
"un_elevated": "게임 비상승 실행(관리자 없음)"
},
"downloads": {
"grasscutter_fullbuild": "올인원 Grasscutter 다운로드",
@@ -44,7 +45,9 @@
"resources": "리소스 다운로드",
"game": "게임 다운로드",
"aio_header": "올인원 다운로드",
"individual_header": "개별 부품 다운로드:"
"individual_header": "개별 부품 다운로드:",
"mods_header": "Mods:",
"migoto": "GIMI 3dmigoto 다운로드"
},
"download_status": {
"downloading": "다운로드 중",
@@ -76,7 +79,9 @@
"resources": "또한 Grasscutter 서버를 실행하는 데도 필요합니다. 내용이 포함된 기존 리소스 폴더가 있는 경우 이 버튼은 회색으로 표시됩니다",
"emergency_rsa": "문제가 있는 경우 RSA 패치를 제거하십시오.",
"use_proxy": "Culturation 내부 프록시를 사용합니다. 피들러와 같은 것을 사용하지 않는 한 이 기능을 활성화해야 합니다",
"patch_rsa": "게임 RSA를 자동으로 패치 및 패치 해제합니다. 이전/비공식 버전을 사용하거나 RSA를 수동으로 패치하지 않은 경우 이 기능을 활성화해야 합니다."
"patch_rsa": "게임 RSA를 자동으로 패치 및 패치 해제합니다. 이전/비공식 버전을 사용하거나 RSA를 수동으로 패치하지 않은 경우 이 기능을 활성화해야 합니다.",
"add_delay": "3dmigoto 로더에서 지연을 설정하세요! \n이렇게하면 로딩 문제가 해결되지만 게임을 시작할 때 3dmigoto가로드되는시기에 약간의 지연이 추가됩니다. \n이제 3dmigoto로 다시 시작할 수 있습니다.",
"migoto": "GameBanana에서 모델 가져오기"
},
"swag": {
"akebi_name": "Akebi",

View File

@@ -27,7 +27,8 @@
"use_proxy": "Izmantot iekšējo starpniekserveri",
"wipe_login": "Noslaucīt pieteikšanās kešatmiņu",
"horny_mode": "Uzbudināts režīms",
"auto_mongodb": "Automātiski startējiet MongoDB"
"auto_mongodb": "Automātiski startējiet MongoDB",
"un_elevated": "Palaist spēli bez paaugstinājuma (bez administratora)"
},
"downloads": {
"grasscutter_fullbuild": "Lejupielādējiet Grasscutter viss vienā",
@@ -41,7 +42,9 @@
"grasscutter_latest_update": "Atjauniet Grasscutter jaunāko",
"resources": "Lejupielādējiet Grasscutter resursi",
"aio_header": "Lejupielādes viss vienā",
"individual_header": "Atsevišķu daļu lejupielādes:"
"individual_header": "Atsevišķu daļu lejupielādes:",
"mods_header": "Mods:",
"migoto": "Lejupielādēt GIMI 3dmigoto"
},
"download_status": {
"downloading": "Notiek lejupielāde",
@@ -68,6 +71,8 @@
"gc_dev_jar": "Lejupielādējiet jaunāko izstrāde Grasscutter versiju, kuram ir jar failu un datu failus.",
"gc_stable_data": "Lejupielādējiet pašreizējos stabilos Grasscutter datu failus, kuriem nav jar fails. Tas ir noderīgi atjaunināšanai.",
"gc_dev_data": "Lejupielādējiet jaunāko izstrāde Grasscutter datu failus, kuriem nav pievienots jar fails. Tas ir noderīgi atjaunināšanai.",
"resources": "Tie ir nepieciešami arī Grasscutter servera darbināšanai. Šī poga būs pelēka, ja jums ir resursu mape ar saturu."
"resources": "Tie ir nepieciešami arī Grasscutter servera darbināšanai. Šī poga būs pelēka, ja jums ir resursu mape ar saturu.",
"add_delay": "Iestatiet kavēšanos 3dmigoto iekrāvē! \nTam vajadzētu novērst ielādes problēmas, bet tas nedaudz aizkavēs 3dmigoto ielādēšanu, uzsākot spēli. \nTagad atkal varat sākt ar 3dmigoto.",
"migoto": "Modeļu importēšanai no GameBanana"
}
}

View File

@@ -28,7 +28,8 @@
"use_proxy": "Gebruik Interne Proxy",
"wipe_login": "Login cache wissen",
"horny_mode": "Geile modus",
"auto_mongodb": "Start automatisch MongoDB"
"auto_mongodb": "Start automatisch MongoDB",
"un_elevated": "Voer het spel uit zonder hoogtevrees (geen admin)"
},
"downloads": {
"grasscutter_fullbuild": "Grasscutter Alles-in-één Downloaden",
@@ -43,7 +44,9 @@
"resources": "Download Grasscutter bronnen",
"game": "Download Spel",
"aio_header": "Alles-in-één Downloads:",
"individual_header": "Downloads van afzonderlijke onderdelen:"
"individual_header": "Downloads van afzonderlijke onderdelen:",
"mods_header": "Mods:",
"migoto": "Download GIMI 3dmigoto"
},
"download_status": {
"downloading": "Aan Het Downloading",
@@ -75,7 +78,9 @@
"resources": "Deze zijn ook nodig om een Grasscutter server te draaien. Deze knop zal grijs zijn als u een bestaande resources map heeft met inhoud erin",
"emergency_rsa": "Voor het geval er iets fout is gegaan, herstel uw rsa naar de laatste offici<63>le versies rsa.",
"use_proxy": "Gebruik de Cultivation interne proxy. U zou dit ingeschakeld moeten hebben, tenzij u iets als Fiddler gebruikt",
"patch_rsa": "Patch en unpatch je spel rsa automatisch. Tenzij je met oude/niet-offici<63>le versies speelt, of je hebt je rsa handmatig gepatcht, zou dit ingeschakeld moeten zijn."
"patch_rsa": "Patch en unpatch je spel rsa automatisch. Tenzij je met oude/niet-offici<63>le versies speelt, of je hebt je rsa handmatig gepatcht, zou dit ingeschakeld moeten zijn.",
"add_delay": "Vertraging instellen in 3dmigoto loader! \nDit zou laadproblemen moeten oplossen, maar zal een kleine vertraging toevoegen aan het laden van 3dmigoto bij het opstarten van het spel. \nJe kunt nu weer starten met 3dmigoto.",
"migoto": "Voor het importeren van modellen uit GameBanana"
},
"swag": {
"akebi_name": "Akebi",

94
src-tauri/lang/pt-br.json Normal file
View File

@@ -0,0 +1,94 @@
{
"lang_name": "Português Brasileiro",
"main": {
"title": "Cultivation: Edição Thorny",
"launch_button": "Iniciar",
"gc_enable": "Conectar ao Grasscutter",
"https_enable": "Usar HTTPS",
"ip_placeholder": "Endereço do Servidor...",
"port_placeholder": "Porta...",
"files_downloading": "Baixando Arquivos: ",
"files_extracting": "Extraindo Arquivos: "
},
"options": {
"enabled": "Habilitado",
"disabled": "Desabilitado",
"game_path": "Definir o Local de Instalação do Jogo",
"game_command": "Comando de Iniciação do Jogo",
"game_executable": "Definir o Executavel do Jogo",
"recover_rsa": "Exclusão de Emergencia de RSA",
"grasscutter_jar": "Definir o arquivo JAR do Grasscutter",
"toggle_encryption": "Ativar/Desativar Criptografia",
"install_certificate": "Instalar o Certificado de Proxy",
"java_path": "Definir um Local Customizado do Java",
"grasscutter_with_game": "Iniciar automaticamente o Grasscutter com o Jogo",
"language": "Selecionar Idioma",
"background": "Definir Fundo Customizado (link ou arquivo de imagem)",
"theme": "Definir Tema",
"patch_rsa": "Automaticamente Corrigir RSA",
"use_proxy": "Usar Proxy Interno",
"wipe_login": "Limpar Cache de Login",
"horny_mode": "Modo com tesão",
"auto_mongodb": "Iniciar MongoDB Automaticamente",
"un_elevated": "Executar o jogo não-elevated (sem admin)"
},
"downloads": {
"grasscutter_fullbuild": "Baixar o Grasscutter Tudo-em-Um",
"grasscutter_stable_data": "Baixar os Dados do Grasscutter Estável",
"grasscutter_latest_data": "Baixar os Dados do Grasscutter Mais Recente",
"grasscutter_stable_data_update": "Atualizar os Dados do Grasscutter Estável",
"grasscutter_latest_data_update": "Atualizar os Dados do Grasscutter Mais Recente",
"grasscutter_stable": "Baixar o Grasscutter Estável",
"grasscutter_latest": "Baixar o Grasscutter Mais Recente",
"grasscutter_stable_update": "Atualizar o Grasscutter Estável",
"grasscutter_latest_update": "Atualizar o Grasscutter Mais Recente",
"resources": "Baixar os Recursos do Grasscutter ",
"game": "Baixar o Jogo",
"aio_header": "Downloads Tudo-em-Um:",
"individual_header": "Downloads Individuais:",
"mods_header": "Mods:",
"migoto": "Baixar o GIMI 3dmigoto"
},
"download_status": {
"downloading": "Baixando",
"extracting": "Extraindo",
"error": "Erro",
"finished": "Finalizado",
"stopped": "Parado"
},
"components": {
"select_file": "Selecione o arquivo ou pasta...",
"select_folder": "Selecione a pasta...",
"download": "Baixar",
"delete": "Deletar",
"install": "Instalar"
},
"news": {
"latest_commits": "Commits Recentes",
"latest_version": "Versão mais Recente"
},
"help": {
"port_help_text": "Certifique-se de que esta é a porta do servidor dispatch, não a porta do jogo. Ela é quase sempre '443'.",
"game_help_text": "Você não precisa de uma cópia do jogo separada para jogar com o Grasscutter. Isso é para, ou desatualizar para a 2.6, ou se você não tiver o jogo instalado.",
"gc_stable_jar": "Baixar a versão atual do Grasscutter estável, que inclui o arquivo jar e os arquivos de dados.",
"gc_fullbuild": "Baixar uma versão completa do Grasscutter, incluindo a repo, jar e recursos. Ela está totalmente configurada e não requer nenhum outro download deste menu.",
"gc_dev_jar": "Baixar a versão de desenvolvimento mais recente do Grasscutter, que inclui o arquivo jar e os arquivos de dados.",
"gc_stable_data": "Baixar os arquivos de dados da versão atual do Grasscutter estável, que não inclui o arquivo jar. Isso é útil para atualizações.",
"gc_dev_data": "Baixar os arquivos de dados da versão de desenvolvimento mais recente do Grasscutter, que não vem com um arquivo jar. Isso é útil para atualizações.",
"encryption": "Isso normalmente deve estar desativado.",
"resources": "Esses também são necessários para usar o Grasscutter. Esse botão ficará cinza caso você já tenha uma pasta resources com coisas dentro.",
"emergency_rsa": "Caso algo dê errado, força a exclusão da correção RSA.",
"use_proxy": "Usa o proxy interno do Cultivation. Isso deveria estar habilitado a não ser que você utilize algo como o Fiddler.",
"patch_rsa": "Corrigir e 'descorrigir' o RSA do seu jogo automaticamente. Isso deve estar habilitado a não ser que você esteja jogando com versões antigas (3.0 ou mais antigas) ou não oficiais.",
"add_delay": "Atraso definido na carregadeira 3dmigoto! \nIsto deve resolver os problemas de carregamento, mas acrescentará um pequeno atraso quando o 3dmigoto for carregado no lançamento do jogo. \nAgora você pode lançar novamente com o 3dmigoto.",
"migoto": "Para importação de modelos da GameBanana"
},
"swag": {
"akebi_name": "Akebi",
"migoto_name": "Migoto",
"reshade_name": "Reshade",
"akebi": "Definir o Executavel do Akebi/Acrepi",
"migoto": "Definir o Executavel do 3DMigoto",
"reshade": "Definir o Executavel do Reshade Injector"
}
}

View File

@@ -28,7 +28,8 @@
"use_proxy": "Использовать встроенный Прокси",
"wipe_login": "Очистить кэш входа в систему",
"horny_mode": "роговой режим",
"auto_mongodb": "Автоматически запускать MongoDB"
"auto_mongodb": "Автоматически запускать MongoDB",
"un_elevated": "Запустите игру в неэлегантном режиме (без администратора)"
},
"downloads": {
"grasscutter_fullbuild": "Скачать все в одном Grasscutter",
@@ -43,7 +44,9 @@
"resources": "Скачать ресурсы Grasscutter",
"game": "Скачать Игру",
"aio_header": "Все в одной загрузке:",
"individual_header": "загрузка отдельных частей:"
"individual_header": "загрузка отдельных частей:",
"mods_header": "Mods:",
"migoto": "Скачать GIMI 3dmigoto"
},
"download_status": {
"downloading": "Скачивание",
@@ -75,7 +78,9 @@
"resources": "Это необходимо для запуска сервера Grasscutter. Эта кнопка будет серой, если у Вас уже есть не пустая папка с ресурсами.",
"emergency_rsa": "Если что-то пошло не так, восстановит RSA до последней официальной версии.",
"use_proxy": "Использовать встроенный Прокси. Отключите если используете Fiddler или подобную программу",
"patch_rsa": "Патчит и восстанавливает RSA автоматически. Если вы не играете на старых/модифицированых версиях, или сами в ручную патчите Метаданные, эта опция должна быть включена."
"patch_rsa": "Патчит и восстанавливает RSA автоматически. Если вы не играете на старых/модифицированых версиях, или сами в ручную патчите Метаданные, эта опция должна быть включена.",
"add_delay": "Установите задержку в загрузчике 3dmigoto! \nЭто должно исправить проблемы с загрузкой, но добавит небольшую задержку в момент загрузки 3dmigoto при запуске игры. \nТеперь вы снова можете запускать игру с помощью 3dmigoto.",
"migoto": "Для импорта моделей из GameBanana"
},
"swag": {
"akebi_name": "Akebi",

View File

@@ -29,7 +29,8 @@
"use_proxy": "Sử dụng proxy nội bộ",
"wipe_login": "Tẩy sạch cache đăng nhập",
"horny_mode": "Chế độ hứng tình",
"auto_mongodb": "Tự động khởi động MongoDB"
"auto_mongodb": "Tự động khởi động MongoDB",
"un_elevated": "Chạy trò chơi không nâng cao (không có quản trị viên)"
},
"downloads": {
"grasscutter_fullbuild": "Tải Grasscutter tất cả trong một",
@@ -44,7 +45,9 @@
"resources": "Tải tài nguyên Grasscutter",
"game": "Tải game",
"aio_header": "Tải xuống tất cả trong một:",
"individual_header": "Tải xuống từng phần:"
"individual_header": "Tải xuống từng phần:",
"mods_header": "Mods:",
"migoto": "Tải GIMI 3dmigoto"
},
"download_status": {
"downloading": "Đang tải",
@@ -76,7 +79,9 @@
"resources": "Tài nguyên được yêu cầu để chạy máy chủ Grasscutter. Nút này sẽ có màu xám nếu bạn đã có sẵn một thư mục tài nguyên (resources) có nội dung bên trong",
"emergency_rsa": "Trong trường hợp gặp vấn đề, khôi phục lại RSA về phiên bản chính thức mới nhất.",
"use_proxy": "Sử dụng proxy nội bộ của Cultivation. Nên bật tùy chọn này trừ khi bạn đang sử dụng một ứng dụng khác, như Fiddler",
"patch_rsa": "Tự động vá và sửa lại RSA của game. Tùy chọn này nên được bật trừ khi bạn đang sử dụng phiên bản cũ, phiên bản không chính thức hoặc bạn đã tự vá rsa rồi."
"patch_rsa": "Tự động vá và sửa lại RSA của game. Tùy chọn này nên được bật trừ khi bạn đang sử dụng phiên bản cũ, phiên bản không chính thức hoặc bạn đã tự vá rsa rồi.",
"add_delay": "Đặt độ trễ trong trình tải 3dmigoto! \nĐiều này sẽ khắc phục sự cố tải, nhưng sẽ thêm một độ trễ nhỏ khi 3dmigoto được tải khi khởi chạy trò chơi. \nBây giờ bạn có thể khởi chạy lại với 3dmigoto.",
"migoto": "Để nhập mô hình từ GameBanana"
},
"swag": {
"akebi_name": "Akebi",

44
src-tauri/src/config.rs Normal file
View File

@@ -0,0 +1,44 @@
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::string::String;
#[derive(Serialize, Deserialize, Debug)]
pub struct Configuration {
pub toggle_grasscutter: bool,
pub game_install_path: String,
pub grasscutter_with_game: bool,
pub grasscutter_path: String,
pub java_path: String,
pub close_action: u64,
pub startup_launch: bool,
pub last_ip: String,
pub last_port: String,
pub language: String,
pub customBackground: String,
pub cert_generated: bool,
pub theme: String,
pub https_enabled: bool,
pub debug_enabled: bool,
pub patch_rsa: bool,
pub use_internal_proxy: bool,
pub wipe_login: bool,
pub horny_mode: bool,
pub auto_mongodb: bool,
pub un_elevated: bool,
}
pub fn config_path() -> PathBuf {
let mut path = tauri::api::path::data_dir().unwrap();
path.push("cultivation");
path.push("configuration.json");
path
}
pub fn get_config() -> Configuration {
let path = config_path();
let config = std::fs::read_to_string(path).unwrap();
let config: Configuration = serde_json::from_str(&config).unwrap();
config
}

View File

@@ -28,7 +28,12 @@ pub async fn get_languages() -> std::collections::HashMap<String, String> {
for entry in lang_files {
let entry = entry.unwrap();
let path = entry.path();
let filename = path.file_name().unwrap().to_str().unwrap();
let filename = path
.file_name()
.unwrap_or_else(|| panic!("Failed to get filename from path: {:?}", path))
.to_str()
.unwrap_or_else(|| panic!("Failed to convert filename to string: {:?}", path))
.to_string();
let content = match std::fs::read_to_string(&path) {
Ok(x) => x,

View File

@@ -3,8 +3,11 @@
windows_subsystem = "windows"
)]
use args::{Args, ArgsError};
use file_helpers::dir_exists;
use once_cell::sync::Lazy;
use proxy::set_proxy_addr;
use std::fs;
use std::io::Write;
use std::{collections::HashMap, sync::Mutex};
@@ -18,11 +21,14 @@ use sysinfo::{Pid, ProcessExt, System, SystemExt};
use crate::admin::reopen_as_admin;
mod admin;
mod config;
mod downloader;
mod file_helpers;
mod gamebanana;
mod lang;
mod patch;
mod proxy;
mod release;
mod system_helpers;
mod unzip;
mod web;
@@ -35,24 +41,116 @@ fn try_flush() {
std::io::stdout().flush().unwrap_or(())
}
fn has_arg(args: &[String], arg: &str) -> bool {
args.contains(&arg.to_string())
}
async fn parse_args(inp: &Vec<String>) -> Result<Args, ArgsError> {
let mut args = Args::new(
"Cultivation",
"Private server helper program for an Anime Game",
);
args.flag("h", "help", "Print various CLI args");
args.flag("p", "proxy", "Start the proxy server");
args.flag("G", "launch-game", "Launch the game");
args.flag(
"A",
"no-admin",
"Launch without requiring admin permissions",
);
args.flag(
"g",
"no-gui",
"Run in CLI mode. Requires -A to be passed as well.",
);
args.flag("s", "server", "Launch the configured GC server");
args.flag(
"P",
"patch",
"Patch your game before launching, with whatever your game version needs",
);
args.flag(
"N",
"non-elevated-game",
"Launch the game without admin permissions",
);
args.option(
"H",
"host",
"Set host to connect to (eg. 'localhost:443' or 'my.awesomeserver.com:6969)",
"SERVER_HOST",
getopts::Occur::Optional,
None,
);
args.option(
"a",
"game-args",
"Arguments to pass to the game process, if launching it",
r#""-opt-one -opt-two""#,
getopts::Occur::Optional,
None,
);
async fn arg_handler(args: &[String]) {
if has_arg(args, "--proxy") {
args.parse(inp).unwrap();
let config = config::get_config();
if args.value_of("help")? {
println!("{}", args.full_usage());
std::process::exit(0);
}
if args.value_of("launch-game")? {
let game_path = config.game_install_path;
let game_args: String = args.value_of("game-args").unwrap_or(String::new());
// Patch if needed
if args.value_of("patch")? {
patch::patch_game().await;
}
if args.value_of("non-elevated-game")? {
system_helpers::run_un_elevated(game_path.to_string(), Some(game_args))
} else {
system_helpers::run_program(game_path.to_string(), Some(game_args))
}
}
if args.value_of("server")? {
let server_jar = config.grasscutter_path;
let mut server_path = server_jar.clone();
// Strip jar name from path
if server_path.contains('/') {
// Can never panic because of if
let len = server_jar.rfind('/').unwrap();
server_path.truncate(len);
} else if server_path.contains('\\') {
let len = server_jar.rfind('\\').unwrap();
server_path.truncate(len);
}
let java_path = config.java_path;
system_helpers::run_jar(server_jar, server_path.to_string(), java_path);
}
if args.value_of::<String>("host").is_ok() && !args.value_of::<String>("host")?.is_empty() {
let host = args.value_of::<String>("host")?;
set_proxy_addr(host);
}
if args.value_of("proxy")? {
println!("Starting proxy server...");
let mut pathbuf = tauri::api::path::data_dir().unwrap();
pathbuf.push("cultivation");
pathbuf.push("ca");
connect(8035, pathbuf.to_str().unwrap().to_string()).await;
}
Ok(args)
}
fn main() {
fn main() -> Result<(), ArgsError> {
let args: Vec<String> = std::env::args().collect();
let parsed_args = block_on(parse_args(&args)).unwrap();
if !is_elevated() && !has_arg(&args, "--no-admin") {
if !is_elevated() && !parsed_args.value_of("no-admin")? {
println!("===============================================================================");
println!("You running as a non-elevated user. Some stuff will almost definitely not work.");
println!("===============================================================================");
@@ -70,16 +168,15 @@ fn main() {
exe_path.pop();
std::env::set_current_dir(&exe_path).unwrap();
block_on(arg_handler(&args));
// For disabled GUI
ctrlc::set_handler(|| {
disconnect();
block_on(patch::unpatch_game());
std::process::exit(0);
})
.unwrap_or(());
if !has_arg(&args, "--no-gui") {
if !parsed_args.value_of("no-gui")? {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
enable_process_watcher,
@@ -102,10 +199,13 @@ fn main() {
system_helpers::install_location,
system_helpers::is_elevated,
system_helpers::set_migoto_target,
system_helpers::set_migoto_delay,
system_helpers::wipe_registry,
system_helpers::get_platform,
system_helpers::run_un_elevated,
proxy::set_proxy_addr,
proxy::generate_ca_files,
release::get_latest_release,
unzip::unzip,
file_helpers::rename,
file_helpers::dir_create,
@@ -138,6 +238,11 @@ fn main() {
// Always disconnect upon closing the program
disconnect();
// Always unpatch game upon closing the program
block_on(patch::unpatch_game());
Ok(())
}
#[tauri::command]
@@ -210,11 +315,11 @@ fn restart_grasscutter(window: tauri::Window) -> bool {
// Kill it
if process.kill() {
// Also kill the cmd it was open in
if let Some(parent) = system.process(Pid::from(process.parent().unwrap())) {
if let Some(parent) = system.process(process.parent().unwrap()) {
parent.kill();
}
for process_gc in system.processes_by_name("java") {
if process_gc.cmd().last().unwrap().contains(&"grasscutter") {
if process_gc.cmd().last().unwrap().contains("grasscutter") {
process_gc.kill();
}
}

59
src-tauri/src/patch.rs Normal file
View File

@@ -0,0 +1,59 @@
use crate::config;
use crate::file_helpers;
use crate::system_helpers;
use std::path::PathBuf;
pub async fn patch_game() -> bool {
let patch_path = PathBuf::from(system_helpers::install_location()).join("patch/version.dll");
// Are we already patched with mhypbase? If so, that's fine, just continue as normal
let game_is_patched = file_helpers::are_files_identical(
patch_path.clone().to_str().unwrap(),
PathBuf::from(get_game_rsa_path().await.unwrap())
.join("mhypbase.dll")
.to_str()
.unwrap(),
);
// Tell user they won't be unpatched with manual mhypbase patch
if game_is_patched {
println!(
"You are already patched using mhypbase, so you will not be auto patched and unpatched!"
);
return true;
}
// Copy the patch to game files
let replaced = file_helpers::copy_file_with_new_name(
patch_path.clone().to_str().unwrap().to_string(),
get_game_rsa_path().await.unwrap(),
String::from("version.dll"),
);
if !replaced {
return false;
}
true
}
pub async fn unpatch_game() -> bool {
// Just delete patch since it's not replacing any existing file
let deleted = file_helpers::delete_file(
PathBuf::from(get_game_rsa_path().await.unwrap())
.join("version.dll")
.to_str()
.unwrap()
.to_string(),
);
deleted
}
pub async fn get_game_rsa_path() -> Option<String> {
let config = config::get_config();
let mut game_folder = PathBuf::from(config.game_install_path);
game_folder.pop();
Some(format!("{}/", game_folder.to_str().unwrap()).replace('\\', "/"))
}

View File

@@ -41,7 +41,14 @@ struct ProxyHandler;
#[tauri::command]
pub fn set_proxy_addr(addr: String) {
*SERVER.lock().unwrap() = addr;
if addr.contains(' ') {
let addr2 = addr.replace(' ', "");
*SERVER.lock().unwrap() = addr2;
} else {
*SERVER.lock().unwrap() = addr;
}
println!("Set server to {}", SERVER.lock().unwrap());
}
#[async_trait]
@@ -86,7 +93,9 @@ impl HttpHandler for ProxyHandler {
}
async fn should_intercept(&mut self, _ctx: &HttpContext, _req: &Request<Body>) -> bool {
true
let uri = _req.uri().to_string();
uri.contains("hoyoverse.com") || uri.contains("mihoyo.com") || uri.contains("yuanshen.com")
}
}
@@ -165,7 +174,8 @@ pub fn connect_to_proxy(proxy_port: u16) {
let settings = Hive::CurrentUser
.open(
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
Security::Write,
// Only write should be needed but too many cases of Culti not being able to read/write proxy settings
Security::AllAccess,
)
.unwrap();
@@ -212,7 +222,7 @@ pub fn disconnect_from_proxy() {
let settings = Hive::CurrentUser
.open(
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
Security::Write,
Security::AllAccess,
)
.unwrap();

32
src-tauri/src/release.rs Normal file
View File

@@ -0,0 +1,32 @@
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Release {
pub tag_name: String,
pub link: String,
}
#[tauri::command]
pub async fn get_latest_release() -> Release {
let url = "https://api.github.com/repos/Grasscutters/Cultivation/releases/latest";
let client = reqwest::Client::new();
let response = client
.get(url)
.header("User-Agent", "Cultivation")
.send()
.await
.unwrap();
let text = response.text().await.unwrap();
println!("Response: {}", text);
// Parse "tag_name" from JSON
let json: serde_json::Value = serde_json::from_str(&text).unwrap();
let tag_name = json["tag_name"].as_str().unwrap();
// Parse "html_url"
let link = json["html_url"].as_str().unwrap();
Release {
tag_name: tag_name.to_string(),
link: link.to_string(),
}
}

View File

@@ -11,7 +11,13 @@ use registry::{Data, Hive, Security};
#[tauri::command]
pub fn run_program(path: String, args: Option<String>) {
// Without unwrap_or, this can crash when UAC prompt is denied
open::that(format!("{} {}", &path, &args.unwrap_or_else(|| "".into()))).unwrap_or(());
match open::with(
format!("{} {}", path, args.unwrap_or_else(|| "".into())),
path.clone(),
) {
Ok(_) => (),
Err(e) => println!("Failed to open program ({}): {}", &path, e),
};
}
#[tauri::command]
@@ -67,6 +73,8 @@ pub fn run_jar(path: String, execute_in: String, java_path: String) {
format!("\"{}\" -jar \"{}\"", java_path, path)
};
println!("Launching .jar with command: {}", &command);
// Open the program from the specified path.
match open::with(
format!("/k cd /D \"{}\" & {}", &execute_in, &command),
@@ -77,6 +85,22 @@ pub fn run_jar(path: String, execute_in: String, java_path: String) {
};
}
#[tauri::command]
pub fn run_un_elevated(path: String, args: Option<String>) {
// Open the program non-elevated.
match open::with(
format!(
"cmd /min /C \"set __COMPAT_LAYER=RUNASINVOKER && start \"\" \"{}\"\" {}",
path,
args.unwrap_or_else(|| "".into())
),
"C:\\Windows\\System32\\cmd.exe",
) {
Ok(_) => (),
Err(e) => println!("Failed to open program ({}): {}", &path, e),
};
}
#[tauri::command]
pub fn open_in_browser(url: String) {
// Open the URL in the default browser.
@@ -97,7 +121,45 @@ pub fn install_location() -> String {
}
#[tauri::command]
pub fn set_migoto_target(migoto_path: String) -> bool {
pub fn set_migoto_target(window: tauri::Window, migoto_path: String) -> bool {
let mut migoto_pathbuf = PathBuf::from(migoto_path);
migoto_pathbuf.pop();
migoto_pathbuf.push("d3dx.ini");
let mut conf = match Ini::load_from_file(&migoto_pathbuf) {
Ok(c) => {
println!("Loaded migoto ini");
c
}
Err(e) => {
println!("Error loading migoto config: {}", e);
return false;
}
};
window.emit("migoto_set", &()).unwrap();
// Set options
conf
.with_section(Some("Loader"))
.set("target", "GenshinImpact.exe");
// Write file
match conf.write_to_file(&migoto_pathbuf) {
Ok(_) => {
println!("Wrote config!");
true
}
Err(e) => {
println!("Error writing config: {}", e);
false
}
}
}
#[tauri::command]
pub fn set_migoto_delay(migoto_path: String) -> bool {
let mut migoto_pathbuf = PathBuf::from(migoto_path);
migoto_pathbuf.pop();
@@ -115,18 +177,16 @@ pub fn set_migoto_target(migoto_path: String) -> bool {
};
// Set options
conf
.with_section(Some("Loader"))
.set("target", "GenshinImpact.exe");
conf.with_section(Some("Loader")).set("delay", "20");
// Write file
match conf.write_to_file(&migoto_pathbuf) {
Ok(_) => {
println!("Wrote config!");
println!("Wrote delay!");
true
}
Err(e) => {
println!("Error writing config: {}", e);
println!("Error writing delay: {}", e);
false
}
}

View File

@@ -72,6 +72,10 @@ pub fn unzip(
let archive = Archive::new(zipfile.clone());
name = archive.list().unwrap().next().unwrap().unwrap().filename;
} else if zipfile.ends_with(".7z") {
success = extract_7z(&zipfile, &f, &full_path, top_level.unwrap_or(true));
name = String::from("banana");
} else {
success = extract_zip(&zipfile, &f, &full_path, top_level.unwrap_or(true));
@@ -103,6 +107,15 @@ pub fn unzip(
.unwrap();
}
if zipfile.contains("GIMI") {
window
.emit(
"migoto_extracted",
destpath.to_string() + "3DMigoto Loader.exe",
)
.unwrap();
}
// Delete zip file
match std::fs::remove_file(&zipfile) {
Ok(_) => {
@@ -169,3 +182,20 @@ fn extract_zip(_zipfile: &str, f: &File, full_path: &path::Path, top_level: bool
}
}
}
fn extract_7z(sevenzfile: &str, _f: &File, full_path: &path::Path, _top_level: bool) -> bool {
match sevenz_rust::decompress_file(sevenzfile, full_path) {
Ok(_) => {
println!(
"Extracted 7zip file to: {}",
full_path.to_str().unwrap_or("Error")
);
true
}
Err(e) => {
println!("Failed to extract 7zip file: {}", e);
false
}
}
}

View File

@@ -7,7 +7,7 @@
},
"package": {
"productName": "Cultivation",
"version": "1.0.23"
"version": "1.0.26"
},
"tauri": {
"allowlist": {

View File

@@ -11,9 +11,12 @@ import Downloads from './components/menu/Downloads'
import NewsSection from './components/news/NewsSection'
import Game from './components/menu/Game'
import RightBar from './components/RightBar'
import { ExtrasMenu } from './components/menu/ExtrasMenu'
import Notification from './components/common/Notification'
import { getConfigOption, setConfigOption } from '../utils/configuration'
import { invoke } from '@tauri-apps/api'
import { getVersion } from '@tauri-apps/api/app'
import { listen } from '@tauri-apps/api/event'
import { dataDir } from '@tauri-apps/api/path'
import { appWindow } from '@tauri-apps/api/window'
@@ -24,7 +27,6 @@ import DownloadHandler from '../utils/download'
import cogBtn from '../resources/icons/cog.svg'
import downBtn from '../resources/icons/download.svg'
import wrenchBtn from '../resources/icons/wrench.svg'
import { ExtrasMenu } from './components/menu/ExtrasMenu'
interface IProps {
downloadHandler: DownloadHandler
@@ -39,6 +41,7 @@ interface IState {
extrasOpen: boolean
migotoSet: boolean
playGame: (exe?: string, proc_name?: string) => void
notification: React.ReactElement | null
}
export class Main extends React.Component<IProps, IState> {
@@ -55,6 +58,7 @@ export class Main extends React.Component<IProps, IState> {
playGame: () => {
alert('Error launching game')
},
notification: null,
}
listen('lang_error', (payload) => {
@@ -65,6 +69,14 @@ export class Main extends React.Component<IProps, IState> {
setConfigOption('grasscutter_path', payload)
})
listen('migoto_extracted', ({ payload }: { payload: string }) => {
setConfigOption('migoto_path', payload)
invoke('set_migoto_target', {
migotoPath: payload,
})
})
// Emitted for rsa replacing-purposes
listen('game_closed', async () => {
const wasPatched = await getConfigOption('patch_rsa')
@@ -78,6 +90,14 @@ export class Main extends React.Component<IProps, IState> {
}
})
listen('migoto_set', async () => {
this.setState({
migotoSet: !!(await getConfigOption('migoto_path')),
})
window.location.reload()
})
// Emitted for automatic processes
listen('grasscutter_closed', async () => {
const autoService = await getConfigOption('auto_mongodb')
@@ -125,6 +145,35 @@ export class Main extends React.Component<IProps, IState> {
const updatedConfig = await getConfigOption('patch_rsa')
await setConfigOption('patch_rsa', updatedConfig)
// Get latest version and compare to this version
const latestVersion: {
tag_name: string
link: string
} = await invoke('get_latest_release')
const tagName = latestVersion?.tag_name.replace(/[^\d.]/g, '')
// Check if tagName is different than current version
if (tagName && tagName !== (await getVersion())) {
// Display notification of new release
this.setState({
notification: (
<>
Cultivation{' '}
<a href="#" onClick={() => invoke('open_in_browser', { url: latestVersion.link })}>
{latestVersion?.tag_name}
</a>{' '}
is now available!
</>
),
})
setTimeout(() => {
this.setState({
notification: null,
})
}, 6000)
}
// Period check to only show progress bar when downloading files
setInterval(() => {
this.setState({
@@ -176,6 +225,8 @@ export class Main extends React.Component<IProps, IState> {
</div> */}
</TopBar>
<Notification show={!!this.state.notification}>{this.state.notification}</Notification>
<RightBar />
<NewsSection />

View File

@@ -39,6 +39,8 @@ interface IState {
swag: boolean
akebiSet: boolean
migotoSet: boolean
unElevated: boolean
}
export default class ServerLaunchSection extends React.Component<IProps, IState> {
@@ -62,6 +64,7 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
swag: false,
akebiSet: false,
migotoSet: false,
unElevated: false,
}
this.toggleGrasscutter = this.toggleGrasscutter.bind(this)
@@ -93,6 +96,7 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
swag: config.swag_mode || false,
akebiSet: config.akebi_path !== '',
migotoSet: config.migoto_path !== '',
unElevated: config.un_elevated || false,
})
}
@@ -188,7 +192,14 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
path: exe || config.game_install_path,
})
if (gameExists) await invoke('run_program_relative', { path: exe || config.game_install_path })
if (gameExists)
if (config.un_elevated) {
await invoke('run_un_elevated', {
path: config.game_install_path,
})
} else {
await invoke('run_program_relative', { path: exe || config.game_install_path })
}
else alert('Game not found! At: ' + (exe || config.game_install_path))
}

View File

@@ -1,5 +1,5 @@
.DirInput {
display: flex;
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: center;

View File

@@ -0,0 +1,24 @@
.Notification {
position: absolute;
/* Default styles, changed when showing notif */
top: -100%;
right: 10%;
padding: 20px;
border-radius: 4px;
border: 1px solid #ffc61e;
background-color: #fff;
color: #000;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.2s ease;
}
.NotifShow {
top: 10%;
}

View File

@@ -0,0 +1,22 @@
import React from 'react'
import './Notification.css'
interface IProps {
children: React.ReactNode | null
show: boolean
}
export default class Notification extends React.Component<IProps> {
constructor(props: IProps) {
super(props)
}
render() {
return (
<div className={'Notification ' + (this.props.show ? 'NotifShow' : '')}>
<div className="NotificationMessage">{this.props.children}</div>
</div>
)
}
}

View File

@@ -0,0 +1,32 @@
.SmallButtonSection {
display: inline-block;
margin-left: 20px;
vertical-align: middle;
}
.SmallButtonButton {
height: 20px;
filter: drop-shadow(0px 0px 5px rgb(0 0 0 / 20%));
}
.SmallButtonButton:hover {
cursor: pointer;
}
.SmallButtonButton img {
height: 100%;
}
.SmallButtonContents {
text-align: center;
position: relative;
}
.SmallButtonContents .MiniDialog {
position: absolute;
bottom: 40px;
right: -450%;
width: 200px;
height: 120px;
}

View File

@@ -0,0 +1,39 @@
import React from 'react'
import './SmallButton.css'
import Wrench from '../../../resources/icons/wrench.svg'
import { translate } from '../../../utils/language'
interface IProps {
children?: React.ReactNode[] | React.ReactNode
onClick: () => unknown
id?: string
contents?: string
}
export default class SmallButton extends React.Component<IProps> {
constructor(props: IProps) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
async showAlert() {
if (this.props.contents) alert(await translate(this.props.contents))
}
handleClick() {
this.props.onClick()
this.showAlert()
}
render() {
return (
<div className="SmallButtonSection">
<div className="SmallButtonButton" onClick={this.handleClick}>
<img src={Wrench} />
</div>
</div>
)
}
}

View File

@@ -19,6 +19,8 @@ const DEV_REPO_DOWNLOAD = 'https://github.com/Grasscutters/Grasscutter/archive/r
const STABLE_DOWNLOAD = 'https://nightly.link/Grasscutters/Grasscutter/workflows/build/stable/Grasscutter.zip'
const DEV_DOWNLOAD = 'https://nightly.link/Grasscutters/Grasscutter/workflows/build/development/Grasscutter.zip'
const RESOURCES_DOWNLOAD = 'https://gitlab.com/YuukiPS/GC-Resources/-/archive/3.5/GC-Resources-3.5.zip' // Use Yuuki res as grasscutter crepe res are broken
const MIGOTO_DOWNLOAD =
'https://github.com/SilentNightSound/GI-Model-Importer/releases/download/V6.0/3dmigoto-GIMI-for-playing-mods.zip'
interface IProps {
closeFn: () => void
@@ -30,8 +32,10 @@ interface IState {
grasscutter_downloading: boolean
resources_downloading: boolean
repo_downloading: boolean
migoto_downloading: boolean
grasscutter_set: boolean
resources_exist: boolean
swag: boolean
}
export default class Downloads extends React.Component<IProps, IState> {
@@ -43,8 +47,10 @@ export default class Downloads extends React.Component<IProps, IState> {
grasscutter_downloading: this.props.downloadManager.downloadingJar(),
resources_downloading: this.props.downloadManager.downloadingResources(),
repo_downloading: this.props.downloadManager.downloadingRepo(),
migoto_downloading: this.props.downloadManager.downloadingMigoto(),
grasscutter_set: false,
resources_exist: false,
swag: false,
}
this.getGrasscutterFolder = this.getGrasscutterFolder.bind(this)
@@ -54,11 +60,17 @@ export default class Downloads extends React.Component<IProps, IState> {
this.downloadGrasscutterStable = this.downloadGrasscutterStable.bind(this)
this.downloadGrasscutterLatest = this.downloadGrasscutterLatest.bind(this)
this.downloadResources = this.downloadResources.bind(this)
this.downloadMigoto = this.downloadMigoto.bind(this)
this.toggleButtons = this.toggleButtons.bind(this)
}
async componentDidMount() {
const gc_path = await getConfigOption('grasscutter_path')
const swag = await getConfigOption('swag_mode')
this.setState({
swag: swag || false,
})
listen('jar_extracted', () => {
this.setState({ grasscutter_set: true }, this.forceUpdate)
@@ -113,6 +125,12 @@ export default class Downloads extends React.Component<IProps, IState> {
return folderPath
}
async getCultivationFolder() {
const folderPath = (await dataDir()) + 'cultivation'
return folderPath
}
async downloadGrasscutterFullBuild() {
const folder = await this.getGrasscutterFolder()
this.props.downloadManager.addDownload(FULL_BUILD_DOWNLOAD, folder + '\\GrasscutterCulti.zip', async () => {
@@ -201,6 +219,20 @@ export default class Downloads extends React.Component<IProps, IState> {
this.toggleButtons()
}
async downloadMigoto() {
const folder = (await this.getCultivationFolder()) + '\\3dmigoto'
await invoke('dir_create', {
path: folder,
})
this.props.downloadManager.addDownload(MIGOTO_DOWNLOAD, folder + '\\GIMI-3dmigoto.zip', async () => {
await unzip(folder + '\\GIMI-3dmigoto.zip', folder + '\\', true)
this.toggleButtons()
})
this.toggleButtons()
}
async toggleButtons() {
const gc_path = await getConfigOption('grasscutter_path')
@@ -210,6 +242,7 @@ export default class Downloads extends React.Component<IProps, IState> {
grasscutter_downloading: this.props.downloadManager.downloadingJar(),
resources_downloading: this.props.downloadManager.downloadingResources(),
repo_downloading: this.props.downloadManager.downloadingRepo(),
migoto_downloading: this.props.downloadManager.downloadingMigoto(),
grasscutter_set: gc_path !== '',
})
}
@@ -336,6 +369,26 @@ export default class Downloads extends React.Component<IProps, IState> {
</BigButton>
</div>
</div>
{this.state.swag && (
<>
<Divider />
<div className="HeaderText" id="downloadMenuModsHeader">
<Tr text="downloads.mods_header" />
</div>
<div className="DownloadMenuSection" id="downloadMenuContainerMigoto">
<div className="DownloadLabel" id="downloadMenuLabelMigoto">
<Tr text={'downloads.migoto'} />
<HelpButton contents="help.migoto" />
</div>
<div className="DownloadValue" id="downloadMenuButtonMigoto">
<BigButton disabled={this.state.migoto_downloading} onClick={this.downloadMigoto} id="migotoBtn">
<Tr text="components.download" />
</BigButton>
</div>
</div>
</>
)}
</Menu>
)
}

View File

@@ -3,7 +3,7 @@ import { invoke } from '@tauri-apps/api'
import { dataDir } from '@tauri-apps/api/path'
import DirInput from '../common/DirInput'
import Menu from './Menu'
import Tr, { getLanguages, translate } from '../../../utils/language'
import Tr, { getLanguages } from '../../../utils/language'
import { setConfigOption, getConfig, getConfigOption, Configuration } from '../../../utils/configuration'
import Checkbox from '../common/Checkbox'
import Divider from './Divider'
@@ -16,6 +16,7 @@ import DownloadHandler from '../../../utils/download'
import * as meta from '../../../utils/rsa'
import HelpButton from '../common/HelpButton'
import TextInput from '../common/TextInput'
import SmallButton from '../common/SmallButton'
interface IProps {
closeFn: () => void
@@ -40,6 +41,7 @@ interface IState {
auto_mongodb: boolean
swag: boolean
platform: string
un_elevated: boolean
// Swag stuff
akebi_path: string
@@ -69,6 +71,7 @@ export default class Options extends React.Component<IProps, IState> {
swag: false,
auto_mongodb: false,
platform: '',
un_elevated: false,
// Swag stuff
akebi_path: '',
@@ -85,6 +88,8 @@ export default class Options extends React.Component<IProps, IState> {
this.setCustomBackground = this.setCustomBackground.bind(this)
this.toggleEncryption = this.toggleEncryption.bind(this)
this.removeRSA = this.removeRSA.bind(this)
this.addMigotoDelay = this.addMigotoDelay.bind(this)
this.toggleUnElevatedGame = this.toggleUnElevatedGame.bind(this)
}
async componentDidMount() {
@@ -109,7 +114,7 @@ export default class Options extends React.Component<IProps, IState> {
bg_url_or_path: config.customBackground || '',
themes: (await getThemeList()).map((t) => t.name),
theme: config.theme || 'default',
encryption: await translate(encEnabled ? 'options.enabled' : 'options.disabled'),
encryption: encEnabled || false,
patch_rsa: config.patch_rsa || false,
use_internal_proxy: config.use_internal_proxy || false,
wipe_login: config.wipe_login || false,
@@ -117,6 +122,7 @@ export default class Options extends React.Component<IProps, IState> {
swag: config.swag_mode || false,
auto_mongodb: config.auto_mongodb || false,
platform,
un_elevated: config.un_elevated || false,
// Swag stuff
akebi_path: config.akebi_path || '',
@@ -160,7 +166,7 @@ export default class Options extends React.Component<IProps, IState> {
// Update encryption button when setting new jar
this.setState({
encryption: await translate(encEnabled ? 'options.enabled' : 'options.disabled'),
encryption: encEnabled,
})
window.location.reload()
@@ -262,9 +268,7 @@ export default class Options extends React.Component<IProps, IState> {
await server.toggleEncryption(folderPath + '/config.json')
this.setState({
encryption: await translate(
(await server.encryptionEnabled(folderPath + '/config.json')) ? 'options.enabled' : 'options.disabled'
),
encryption: await server.encryptionEnabled(folderPath + '/config.json'),
})
// Check if Grasscutter is running, and restart if so to apply changes
@@ -274,10 +278,25 @@ export default class Options extends React.Component<IProps, IState> {
}
}
async toggleUnElevatedGame() {
const changedVal = !(await getConfigOption('un_elevated'))
setConfigOption('un_elevated', changedVal)
this.setState({
un_elevated: changedVal,
})
}
async removeRSA() {
await meta.unpatchGame()
}
async addMigotoDelay() {
invoke('set_migoto_delay', {
migotoPath: this.state.migoto_path,
})
}
async installCert() {
await invoke('generate_ca_files', {
path: (await dataDir()) + 'cultivation',
@@ -391,9 +410,7 @@ export default class Options extends React.Component<IProps, IState> {
<HelpButton contents="help.encryption" />
</div>
<div className="OptionValue" id="menuOptionsButtonToggleEnc">
<BigButton onClick={this.toggleEncryption} id="toggleEnc">
{this.state.encryption}
</BigButton>
<Checkbox onChange={() => this.toggleEncryption()} checked={this.state.encryption} id="toggleEnc" />
</div>
</div>
<div className="OptionSection" id="menuOptionsContainerInstallCert">
@@ -422,6 +439,7 @@ export default class Options extends React.Component<IProps, IState> {
<Tr text="swag.migoto" />
</div>
<div className="OptionValue" id="menuOptionsDirMigoto">
<SmallButton onClick={this.addMigotoDelay} id="migotoDelay" contents="help.add_delay"></SmallButton>
<DirInput onChange={this.setMigoto} value={this.state?.migoto_path} extensions={['exe']} />
</div>
</div>
@@ -450,6 +468,18 @@ export default class Options extends React.Component<IProps, IState> {
/>
</div>
</div>
<div className="OptionSection" id="menuOptionsContainerUEGame">
<div className="OptionLabel" id="menuOptionsLabelUEGame">
<Tr text="options.un_elevated" />
</div>
<div className="OptionValue" id="menuOptionsCheckboxUEGame">
<Checkbox
onChange={() => this.toggleOption('un_elevated')}
checked={this.state?.un_elevated}
id="unElevatedGame"
/>
</div>
</div>
{this.state.swag ? (
<div className="OptionSection" id="menuOptionsContainerHorny">
<div className="OptionLabel" id="menuOptionsLabelHorny">

View File

@@ -1,5 +1,6 @@
import React from 'react'
import { ModData, PartialModData } from '../../../utils/gamebanana'
import { getConfigOption } from '../../../utils/configuration'
import './ModTile.css'
import Like from '../../../resources/icons/like.svg'
@@ -18,6 +19,7 @@ interface IProps {
}
interface IState {
horny: boolean
hover: boolean
modEnabled: boolean
}
@@ -27,6 +29,7 @@ export class ModTile extends React.Component<IProps, IState> {
super(props)
this.state = {
horny: false,
hover: false,
modEnabled: false,
}
@@ -44,10 +47,13 @@ export class ModTile extends React.Component<IProps, IState> {
}
async componentDidMount() {
const horny = await getConfigOption('horny_mode')
if (!('id' in this.props.mod)) {
// Partial mod
this.setState({
modEnabled: await modIsEnabled(this.props.mod.name),
horny,
})
return
@@ -55,6 +61,7 @@ export class ModTile extends React.Component<IProps, IState> {
this.setState({
modEnabled: await modIsEnabled(String(this.props.mod.id)),
horny,
})
}
@@ -66,6 +73,7 @@ export class ModTile extends React.Component<IProps, IState> {
this.setState(
{
modEnabled: !this.state.modEnabled,
horny: !this.state.horny,
},
() => {
if (this.state.modEnabled) {
@@ -108,7 +116,7 @@ export class ModTile extends React.Component<IProps, IState> {
))}
<img
src={mod.images[0]}
className={`ModImageInner ${'id' in mod && !this.props.horny && mod.nsfw ? 'nsfw' : ''} ${
className={`ModImageInner ${'id' in mod && !this.state.horny && mod.nsfw ? 'nsfw' : ''} ${
this.state.hover ? 'blur' : ''
}`}
/>

View File

@@ -60,51 +60,51 @@ export default class NewsSection extends React.Component<IProps, IState> {
}
async showLatestCommits() {
// Just use official API
// Just use official API
const response: string = await invoke('req_get', {
url: 'https://api.github.com/repos/Grasscutters/Grasscutter/commits',
})
let grasscutterApiResponse: GrasscutterAPIResponse | null = null
try {
grasscutterApiResponse = JSON.parse(response)
} catch (e) {
grasscutterApiResponse = null
}
let commits: CommitResponse[]
if (grasscutterApiResponse?.commits == null) {
// If it didn't work, try again anyways
const response: string = await invoke('req_get', {
url: 'https://api.github.com/repos/Grasscutters/Grasscutter/commits',
})
let grasscutterApiResponse: GrasscutterAPIResponse | null = null
commits = JSON.parse(response)
} else {
commits = grasscutterApiResponse.commits.gc_stable
}
try {
grasscutterApiResponse = JSON.parse(response)
} catch (e) {
grasscutterApiResponse = null
}
// Probably rate-limited
if (!Array.isArray(commits)) return
let commits: CommitResponse[]
if (grasscutterApiResponse?.commits == null) {
// If it didn't work, try again anyways
const response: string = await invoke('req_get', {
url: 'https://api.github.com/repos/Grasscutters/Grasscutter/commits',
})
commits = JSON.parse(response)
} else {
commits = grasscutterApiResponse.commits.gc_stable
}
// Get only first 5
const commitsList = commits.slice(0, 10)
const commitsListHtml = commitsList.map((commitResponse: CommitResponse) => {
return (
<tr className="Commit" id="newsCommitsTable" key={commitResponse.sha}>
<td className="CommitAuthor">
<span>{commitResponse.commit.author.name}</span>
</td>
<td className="CommitMessage">
<span>{commitResponse.commit.message}</span>
</td>
</tr>
)
})
// Probably rate-limited
if (!Array.isArray(commits)) return
// Get only first 5
const commitsList = commits.slice(0, 10)
const commitsListHtml = commitsList.map((commitResponse: CommitResponse) => {
return (
<tr className="Commit" id="newsCommitsTable" key={commitResponse.sha}>
<td className="CommitAuthor">
<span>{commitResponse.commit.author.name}</span>
</td>
<td className="CommitMessage">
<span>{commitResponse.commit.message}</span>
</td>
</tr>
)
})
this.setState({
commitList: commitsListHtml,
news: <>{commitsListHtml}</>,
})
this.setState({
commitList: commitsListHtml,
news: <>{commitsListHtml}</>,
})
return this.state.commitList
}

View File

@@ -25,6 +25,7 @@ let defaultConfig: Configuration
wipe_login: false,
horny_mode: false,
auto_mongodb: false,
un_elevated: false,
}
})()
@@ -53,6 +54,7 @@ export interface Configuration {
horny_mode: boolean
swag_mode?: boolean
auto_mongodb: boolean
un_elevated: boolean
// Swag stuff
akebi_path?: string

View File

@@ -90,7 +90,7 @@ export default class DownloadHandler {
} = payload
// Find the download that is not extracting and set it's status as such
const index = this.downloads.findIndex((download) => download.path === obj.file || obj.new_folder)
const index = this.downloads.findIndex((download) => download.path === obj.file)
this.downloads[index].status = 'finished'
})
}
@@ -101,21 +101,25 @@ export default class DownloadHandler {
downloadingJar() {
// Kinda hacky but it works
return this.downloads.some((d) => d.path.includes('grasscutter.zip'))
return this.downloads.some((d) => d.path.includes('grasscutter.zip') && d.status != ('finished' || 'error'))
}
downloadingFullBuild() {
// Kinda hacky but it works
return this.downloads.some((d) => d.path.includes('GrasscutterCulti'))
return this.downloads.some((d) => d.path.includes('GrasscutterCulti') && d.status != ('finished' || 'error'))
}
downloadingResources() {
// Kinda hacky but it works
return this.downloads.some((d) => d.path.includes('resources'))
return this.downloads.some((d) => d.path.includes('resources') && d.status != ('finished' || 'error'))
}
downloadingRepo() {
return this.downloads.some((d) => d.path.includes('grasscutter_repo.zip'))
return this.downloads.some((d) => d.path.includes('grasscutter_repo.zip') && d.status != ('finished' || 'error'))
}
downloadingMigoto() {
return this.downloads.some((d) => d.path.includes('3dmigoto') && d.status != ('finished' || 'error'))
}
addDownload(url: string, path: string, onFinish?: () => void) {

View File

@@ -1,25 +1,12 @@
import { invoke } from '@tauri-apps/api'
import { dataDir } from '@tauri-apps/api/path'
import { getGameFolder } from './game'
// Patch file from: https://github.com/34736384/RSAPatch/
export async function patchGame() {
// Do we have a patch already?
const patchedExists = await invoke('dir_exists', {
path: (await getBackupRSAPath()) + '\\version.dll',
})
if (!patchedExists) {
// No patch found? Patching creates one
const patched = await downloadRSA()
if (!patched) {
return false
}
}
const patchPath = (await invoke('install_location')) + '\\patch\\version.dll'
// Are we already patched with mhypbase? If so, that's fine, just continue as normal
const gameIsPatched = await invoke('are_files_identical', {
path1: (await getBackupRSAPath()) + '\\version.dll',
path1: patchPath,
path2: (await getGameRSAPath()) + '\\mhypbase.dll',
})
@@ -31,7 +18,7 @@ export async function patchGame() {
// Copy the patch to game files
const replaced = await invoke('copy_file_with_new_name', {
path: (await getBackupRSAPath()) + '\\version.dll',
path: patchPath,
newPath: await getGameRSAPath(),
newName: 'version.dll',
})
@@ -61,25 +48,3 @@ export async function getGameRSAPath() {
return (gameData + '\\').replace(/\\/g, '/')
}
export async function getBackupRSAPath() {
return (await dataDir()) + 'cultivation\\rsa'
}
export async function downloadRSA() {
// Patch file from: https://github.com/34736384/RSAPatch/
// Should make sure rsa path exists
await invoke('dir_create', {
path: await getBackupRSAPath(),
})
// Copy patch from local for offline compatibility
await invoke('copy_file_with_new_name', {
path: (await invoke('install_location')) + '\\patch\\version.dll',
newPath: await getBackupRSAPath(),
newName: 'version.dll',
})
return true
}