diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index c332b4a6..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "anime-launcher-sdk"] - path = anime-launcher-sdk - url = https://github.com/an-anime-team/anime-launcher-sdk diff --git a/CHANGELOG.md b/CHANGELOG.md index 985a4b10..5527242f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added installation migration feature +- Added game environment switcher +- Added game edition switcher +- Added changelog to updated components toast +- Added wine tools to settings +- Added preferences search +- Added new progress bar statuses for applyign hdiff patches and removing outdated files +- Added automatic 3.5 -> 3.6 voiceover files migration related to changed files structure + +### Fixed + +- Added whitespaces removing from environment values + +### Changed + +- Improved game repairing feature +- Replaced `curl` dependency by native code +- Replaced static image by spinner in wine / dxvk version selection +- Made wine / dxvk versions always visible if they're downloaded + +## [3.3.0] - 24.03.2023 + +### Added + - Added option to use additional xlua patch - Added menu option to open wishes history url diff --git a/Cargo.lock b/Cargo.lock index 948eb7b8..5881b769 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -31,17 +40,18 @@ dependencies = [ [[package]] name = "anime-game-core" -version = "1.4.5" +version = "1.6.1" +source = "git+https://github.com/an-anime-team/anime-game-core?tag=1.6.1#2396d1e6538523c0ea1da2f9e75469fff035e44e" dependencies = [ "anyhow", "bzip2", "cached", - "curl", "flate2", "fs_extra", "kinda-virtual-fs", "lazy_static", "md-5", + "minreq", "serde", "serde_json", "sysinfo", @@ -54,7 +64,7 @@ dependencies = [ [[package]] name = "anime-game-launcher" -version = "3.3.0" +version = "3.4.0" dependencies = [ "anime-launcher-sdk", "anyhow", @@ -76,12 +86,12 @@ dependencies = [ [[package]] name = "anime-launcher-sdk" -version = "0.5.7" +version = "0.5.16" +source = "git+https://github.com/an-anime-team/anime-launcher-sdk?tag=0.5.16#a36215db4613ba284d8b2215ec822df64ce73404" dependencies = [ "anime-game-core", "anyhow", "cached", - "dirs 5.0.0", "discord-rich-presence", "enum-ordinalize", "lazy_static", @@ -97,6 +107,9 @@ name = "anyhow" version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +dependencies = [ + "backtrace", +] [[package]] name = "arc-swap" @@ -223,11 +236,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" -version = "0.13.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "base64ct" @@ -410,6 +438,16 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.3" @@ -487,37 +525,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "curl" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" -dependencies = [ - "curl-sys", - "libc", - "openssl-probe", - "openssl-sys", - "schannel", - "socket2", - "winapi", -] - -[[package]] -name = "curl-sys" -version = "0.4.60+curl-7.88.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "717abe2cb465a5da6ce06617388a3980c9a2844196734bec8ccb8e575250f13f" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "rustls-ffi", - "vcpkg", - "winapi", -] - [[package]] name = "darling" version = "0.14.4" @@ -581,16 +588,7 @@ version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ - "dirs-sys 0.3.7", -] - -[[package]] -name = "dirs" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dece029acd3353e3a58ac2e3eb3c8d6c35827a892edc6cc4138ef9c33df46ecd" -dependencies = [ - "dirs-sys 0.4.0", + "dirs-sys", ] [[package]] @@ -604,17 +602,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "dirs-sys" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04414300db88f70d74c5ff54e50f9e1d1737d9a5b90f53fcf2e95ca2a9ab554b" -dependencies = [ - "libc", - "redox_users", - "windows-sys 0.45.0", -] - [[package]] name = "discord-rich-presence" version = "0.2.3" @@ -1060,6 +1047,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" + [[package]] name = "gio" version = "0.17.4" @@ -1479,18 +1472,6 @@ version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" -[[package]] -name = "libz-sys" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "linux-raw-sys" version = "0.1.4" @@ -1588,6 +1569,22 @@ dependencies = [ "adler", ] +[[package]] +name = "minreq" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41979ac2a5aa373c6e294b4a67fbe5e428e91a4cd0524376681f2bc6d872399b" +dependencies = [ + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "serde", + "serde_json", + "webpki", + "webpki-roots", +] + [[package]] name = "nanorand" version = "0.7.0" @@ -1669,27 +1666,6 @@ dependencies = [ "libc", ] -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "objc" version = "0.2.7" @@ -1719,6 +1695,15 @@ dependencies = [ "objc", ] +[[package]] +name = "object" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.17.1" @@ -1733,9 +1718,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "open" -version = "4.0.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd61e3bf9d78956c72ee864bba52431f7f43994b21a17e9e72596a81bd61075b" +checksum = "075c5203b3a2b698bc72c6c10b1f6263182135751d5013ea66e8a4b3d0562a43" dependencies = [ "pathdiff", ] @@ -1746,19 +1731,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" -[[package]] -name = "openssl-sys" -version = "0.9.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176be2629957c157240f68f61f2d0053ad3a4ecfdd9ebf1e6521d18d9635cf67" -dependencies = [ - "autocfg", - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "ordered-stream" version = "0.0.1" @@ -2145,6 +2117,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a36c42d1873f9a77c53bde094f9664d9891bc604a45b4798fd2c389ed12e5b" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -2187,25 +2165,22 @@ dependencies = [ ] [[package]] -name = "rustls-ffi" -version = "0.8.2" +name = "rustls-native-certs" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9da52707cca59e6eef8a78f3ad8d04024254a168ed1b41eb4dfa9616eace781a" +checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" dependencies = [ - "libc", - "log", - "num_enum", - "rustls", + "openssl-probe", "rustls-pemfile", - "sct", - "webpki", + "schannel", + "security-framework", ] [[package]] name = "rustls-pemfile" -version = "0.2.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ "base64", ] @@ -2250,6 +2225,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "self_cell" version = "0.10.2" @@ -2768,12 +2766,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version-compare" version = "0.1.1" @@ -2894,6 +2886,15 @@ dependencies = [ "untrusted", ] +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3073,7 +3074,7 @@ dependencies = [ "async-trait", "byteorder", "derivative", - "dirs 4.0.0", + "dirs", "enumflags2", "event-listener", "futures-core", diff --git a/Cargo.toml b/Cargo.toml index 4d7b18fb..def4fd55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "anime-game-launcher" -version = "3.3.0" +version = "3.4.0" description = "Anime Game launcher" authors = ["Nikita Podvirnyy "] license = "GPL-3.0" @@ -15,14 +15,19 @@ opt-level = "s" [build-dependencies] glib-build-tools = "0.17" +[dependencies.anime-launcher-sdk] +git = "https://github.com/an-anime-team/anime-launcher-sdk" +tag = "0.5.16" + +# path = "../anime-launcher-sdk" # ! for dev purposes only + [dependencies] relm4 = { version = "0.6.0-alpha.2", features = ["macros", "libadwaita"] } gtk = { package = "gtk4", version = "0.6", features = ["v4_8"] } adw = { package = "libadwaita", version = "0.3", features = ["v1_2"] } -rfd = { version = "0.11", features = ["xdg-portal"], default-features = false } -open = "4.0.0" -anime-launcher-sdk = { path = "anime-launcher-sdk" } +rfd = { version = "0.11", features = ["xdg-portal"], default-features = false } +open = "4.0" tracing = "0.1" tracing-subscriber = "0.3" diff --git a/README.md b/README.md index 4bc73528..310e9453 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,6 @@ This should be automatically enabled if you're using zh_cn (Chinese) as your sys | Folder | Description | | - | - | -| anime-launcher-sdk | Unified core functionality for the launcher | | src | Rust source code | | assets | App assets folder | | assets/locales | App localizations | diff --git a/anime-launcher-sdk b/anime-launcher-sdk deleted file mode 160000 index 19ceddca..00000000 --- a/anime-launcher-sdk +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 19ceddca82367514b5c806cfbc94f527ccd75167 diff --git a/assets/locales/de/errors.ftl b/assets/locales/de/errors.ftl index 75f49c41..7644b571 100644 --- a/assets/locales/de/errors.ftl +++ b/assets/locales/de/errors.ftl @@ -6,6 +6,8 @@ debug-file-opening-error = Debug-Datei konnte nicht geöffnet werden wish-url-search-failed = Kein Wünsche URL gefunden wish-url-opening-error = Wünsche URL konnte nicht geöffnet werden +wine-run-error = Failed to run {$executable} executable using wine + game-launching-failed = Spiel konnte nicht gestartet werden failed-get-selected-wine = Die ausgewählte Wine version konnte nicht abgerufen werden. downloading-failed = Herunterladen fehlgeschlagen diff --git a/assets/locales/de/first_run.ftl b/assets/locales/de/first_run.ftl index 87b72a40..d82bd1fb 100644 --- a/assets/locales/de/first_run.ftl +++ b/assets/locales/de/first_run.ftl @@ -36,12 +36,15 @@ show-all-folders-subtitle = Zusätzliche Pfadauswahl-Einstellungen anzeigen. Tu runners-folder = Runners-Ordner dxvks-folder = DXVKs-Ordner wine-prefix-folder = Wine prefix-Ordner -game-installation-folder = Spiel-Installationsordner +global-game-installation-folder = Global game version installation folder +chinese-game-installation-folder = Chinese game version installation folder fps-unlocker-folder = FPS Unlocker Ordner components-index = Komponentenverzeichnis patch-folder = Patch-Ordner temp-folder = Temp-Ordner +migrate = Migrate + select-voice-packages = Sprachpakete auswählen diff --git a/assets/locales/de/general.ftl b/assets/locales/de/general.ftl index 640ed50f..3fa9c9c1 100644 --- a/assets/locales/de/general.ftl +++ b/assets/locales/de/general.ftl @@ -7,12 +7,22 @@ update-background-description = Lädt das offizielle Hintergrundbild herunter f launcher-language = Launcher Sprache launcher-language-description = Gilt nach Neustart +game-edition = Game edition +global = Global +china = China + +game-environment = Game environment +game-environment-description = Get specific features like additional payment methods + game-voiceovers = Spiel Sprachen +game-voiceovers-description = List of downloaded game voiceovers. You can select them in the game settings english = Englisch japanese = Japanisch korean = Koreanisch chinese = Chinesisch +migrate-installation = Migrate installation +migrate-installation-description = Open special window where you can change your game installation folder repair-game = Spiel Reparieren status = Status @@ -53,6 +63,13 @@ recommended-only = Nur empfohlene wine-version = Wine version wine-recommended-description = Nur empfohlene wine versionen anzeigen +wine-tools = Wine tools +command-line = Command line +registry-editor = Registry editor +explorer = Explorer +task-manager = Task manager +configuration = Configuration + dxvk-version = DXVK version dxvk-selection-disabled = DXVK auswahl ist durch ihre Wine auswahl deaktiviert dxvk-recommended-description = Nur empfohlene dxvk versionen anzeigen diff --git a/assets/locales/de/main.ftl b/assets/locales/de/main.ftl index f58f4fbe..e2c9a862 100644 --- a/assets/locales/de/main.ftl +++ b/assets/locales/de/main.ftl @@ -40,12 +40,17 @@ downloading = Lade Herunter unpacking = Entpacken verifying-files = Verifiziere Dateien repairing-files = Repariere Dateien +migrating-folders = Migrating folders +applying-hdiff = Applying hdiff patches +removing-outdated = Removing outdated files components-index-updated = Kompontentenverzeichnis würde aktualisiert launch = Starten +migrate-folders = Migrate folders +migrate-folders-tooltip = Update game folders structure apply-patch = Patch anwenden download-wine = Wine Herunterladen create-prefix = Prefix erstellen diff --git a/assets/locales/en/errors.ftl b/assets/locales/en/errors.ftl index cb76fc48..8271f849 100644 --- a/assets/locales/en/errors.ftl +++ b/assets/locales/en/errors.ftl @@ -6,6 +6,8 @@ debug-file-opening-error = Failed to open debug file wish-url-search-failed = No wishes url found wish-url-opening-error = Could not open wishes url +wine-run-error = Failed to run {$executable} executable using wine + game-launching-failed = Failed to launch game failed-get-selected-wine = Failed to get selected wine version downloading-failed = Downloading failed diff --git a/assets/locales/en/first_run.ftl b/assets/locales/en/first_run.ftl index 2e719eb4..9ab26c60 100644 --- a/assets/locales/en/first_run.ftl +++ b/assets/locales/en/first_run.ftl @@ -36,12 +36,15 @@ show-all-folders-subtitle = Show additional path selection settings. Do as I say runners-folder = Runners folder dxvks-folder = DXVKs folder wine-prefix-folder = Wine prefix folder -game-installation-folder = Game installation folder +global-game-installation-folder = Global game version installation folder +chinese-game-installation-folder = Chinese game version installation folder fps-unlocker-folder = FPS Unlocker folder components-index = Components index patch-folder = Patch folder temp-folder = Temp folder +migrate = Migrate + select-voice-packages = Select voice packages diff --git a/assets/locales/en/general.ftl b/assets/locales/en/general.ftl index 17e715db..a85cd76f 100644 --- a/assets/locales/en/general.ftl +++ b/assets/locales/en/general.ftl @@ -7,12 +7,22 @@ update-background-description = Download official background picture for the lau launcher-language = Launcher language launcher-language-description = Applies after restart +game-edition = Game edition +global = Global +china = China + +game-environment = Game environment +game-environment-description = Get specific features like additional payment methods + game-voiceovers = Game voiceovers +game-voiceovers-description = List of downloaded game voiceovers. You can select them in the game settings english = English japanese = Japanese korean = Korean chinese = Chinese +migrate-installation = Migrate installation +migrate-installation-description = Open special window where you can change your game installation folder repair-game = Repair game status = Status @@ -53,6 +63,13 @@ recommended-only = Recommended only wine-version = Wine version wine-recommended-description = Show only recommended wine versions +wine-tools = Wine tools +command-line = Command line +registry-editor = Registry editor +explorer = Explorer +task-manager = Task manager +configuration = Configuration + dxvk-version = DXVK version dxvk-selection-disabled = DXVK selection is disabled by your wine group preferences dxvk-recommended-description = Show only recommended dxvk versions diff --git a/assets/locales/en/main.ftl b/assets/locales/en/main.ftl index 8f921d15..f36da468 100644 --- a/assets/locales/en/main.ftl +++ b/assets/locales/en/main.ftl @@ -40,12 +40,17 @@ downloading = Downloading unpacking = Unpacking verifying-files = Verifying files repairing-files = Repairing files +migrating-folders = Migrating folders +applying-hdiff = Applying hdiff patches +removing-outdated = Removing outdated files components-index-updated = Components index was updated launch = Launch +migrate-folders = Migrate folders +migrate-folders-tooltip = Update game folders structure apply-patch = Apply patch download-wine = Download wine create-prefix = Create prefix diff --git a/assets/locales/es/errors.ftl b/assets/locales/es/errors.ftl index 4d9b9c49..97a76a8a 100755 --- a/assets/locales/es/errors.ftl +++ b/assets/locales/es/errors.ftl @@ -6,6 +6,8 @@ debug-file-opening-error = Fallo al abrir el archivo de debug wish-url-search-failed = No se encontró la URL del historial de deseos wish-url-opening-error = No se pudo abrir la URL del historial de deseos +wine-run-error = Fallo al correr el ejecutable {$executable} usando Wine + game-launching-failed = Fallo al iniciar el juego failed-get-selected-wine = Fallo al buscar la versión elegida de Wine downloading-failed = Descarga fallida diff --git a/assets/locales/es/first_run.ftl b/assets/locales/es/first_run.ftl index 6be90811..4dff8d14 100755 --- a/assets/locales/es/first_run.ftl +++ b/assets/locales/es/first_run.ftl @@ -36,12 +36,15 @@ show-all-folders-subtitle = Muestra opciones de selección de rutas adicionales. runners-folder = Carpeta de runners dxvks-folder = Carpeta de DXVKs wine-prefix-folder = Carpeta de prefijo de Wine -game-installation-folder = Carpeta de instalación del juego +global-game-installation-folder = Carpeta de instalación de la edición Global +chinese-game-installation-folder = Carpeta de instalación de la edición China fps-unlocker-folder = Carpeta del liberador de FPS components-index = Índice de componentes patch-folder = Carpeta del parche temp-folder = Carpeta temporal +migrate = Migrar + select-voice-packages = Elegir paquetes de voz diff --git a/assets/locales/es/general.ftl b/assets/locales/es/general.ftl index 0d23d2db..74ca0ffa 100755 --- a/assets/locales/es/general.ftl +++ b/assets/locales/es/general.ftl @@ -7,12 +7,22 @@ update-background-description = Descarga la imagen de fondo oficial para el laun launcher-language = Idioma del launcher launcher-language-description = Se aplica tras un reinicio. +game-edition = Edición del Juego +global = Global +china = China + +game-environment = Entorno del juego +game-environment-description = Da acceso a funciones específicas como medios de pago adicionales + game-voiceovers = Voces del juego +game-voiceovers-description = Lista de voces del juego descargadas. Puedes elegirlas en las opciones del juego english = Inglés japanese = Japonés korean = Coreano chinese = Chino +migrate-installation = Migrar instalación +migrate-installation-description = Abre una ventana especial donde puedes cambiar tu carpeta de instalación del juego repair-game = Reparar juego status = Estado @@ -53,6 +63,13 @@ recommended-only = Sólo recomendadas wine-version = Versión de Wine wine-recommended-description = Mostrar sólo versiones recomendadas de Wine +wine-tools = Herramientas de Wine +command-line = Línea de Comandos +registry-editor = Editor del Registro +explorer = Explorador +task-manager = Administrador de Tareas +configuration = Configuración + dxvk-version = Versión de DXVK dxvk-selection-disabled = La selección de DXVK está deshabilitada por las preferencias de su grupo de vinos dxvk-recommended-description = Mostrar sólo versiones recomendadas de DXVK diff --git a/assets/locales/es/main.ftl b/assets/locales/es/main.ftl index 975fa7f1..3510d72e 100755 --- a/assets/locales/es/main.ftl +++ b/assets/locales/es/main.ftl @@ -40,12 +40,17 @@ downloading = Descargando unpacking = Descomprimiendo verifying-files = Verificación de archivos repairing-files = Reparación de archivos +migrating-folders = Migrating folders +applying-hdiff = Applying hdiff patches +removing-outdated = Removing outdated files components-index-updated = Se actualizó el índice de componentes launch = Iniciar +migrate-folders = Migrate folders +migrate-folders-tooltip = Update game folders structure apply-patch = Aplicar parche download-wine = Descargar wine create-prefix = Crear prefijo diff --git a/assets/locales/fr/errors.ftl b/assets/locales/fr/errors.ftl index 5a442f82..a1fda2ef 100644 --- a/assets/locales/fr/errors.ftl +++ b/assets/locales/fr/errors.ftl @@ -6,6 +6,8 @@ debug-file-opening-error = Impossible d'ouvrir le fichier débug wish-url-search-failed = No wishes url found wish-url-opening-error = Could not open wishes url +wine-run-error = Failed to run {$executable} executable using wine + game-launching-failed = Impossible de lancer le jeu failed-get-selected-wine = Impossible de récupérer la version de wine sélectionnée downloading-failed = Le téléchargement a échoué diff --git a/assets/locales/fr/first_run.ftl b/assets/locales/fr/first_run.ftl index 6fff9989..42cc3b79 100644 --- a/assets/locales/fr/first_run.ftl +++ b/assets/locales/fr/first_run.ftl @@ -36,12 +36,15 @@ show-all-folders-subtitle = Afficher plus de paramètres de sélection d'emplace runners-folder = Emplacement des runners dxvks-folder = Emplacement des versions de DXVK wine-prefix-folder = Emplacement du préfix wine -game-installation-folder = Emplacement d'installation du jeu +global-game-installation-folder = Global game version installation folder +chinese-game-installation-folder = Chinese game version installation folder fps-unlocker-folder = Emplacement des fichiers du débloqueur de FPS components-index = Indice des composants patch-folder = Emplacement du patch temp-folder = Dossier temporaire +migrate = Migrate + select-voice-packages = Sélectionnez les packs de voix diff --git a/assets/locales/fr/general.ftl b/assets/locales/fr/general.ftl index 3384f702..3bed0d96 100644 --- a/assets/locales/fr/general.ftl +++ b/assets/locales/fr/general.ftl @@ -7,12 +7,22 @@ update-background-description = Télécharger l'image de fond du launcher offici launcher-language = Langue du launcher launcher-language-description = S'applique après un redémarrage +game-edition = Game edition +global = Global +china = China + +game-environment = Game environment +game-environment-description = Get specific features like additional payment methods + game-voiceovers = Voiceover en jeu +game-voiceovers-description = List of downloaded game voiceovers. You can select them in the game settings english = Anglais japanese = Japonais korean = Coréen chinese = Chinois +migrate-installation = Migrate installation +migrate-installation-description = Open special window where you can change your game installation folder repair-game = Réparer le jeu status = Statut @@ -53,6 +63,13 @@ recommended-only = Versions recommandées uniquement wine-version = Version de wine wine-recommended-description = N'afficher que les versions recommandées de wine +wine-tools = Wine tools +command-line = Command line +registry-editor = Registry editor +explorer = Explorer +task-manager = Task manager +configuration = Configuration + dxvk-version = Version de DXVK dxvk-selection-disabled = La sélection de versions DXVK est désactivé par vos préférences de groupe wine dxvk-recommended-description = N'afficher que les versions recommandées de DXVK diff --git a/assets/locales/fr/main.ftl b/assets/locales/fr/main.ftl index 2244e865..f473b7dd 100644 --- a/assets/locales/fr/main.ftl +++ b/assets/locales/fr/main.ftl @@ -40,12 +40,17 @@ downloading = Téléchargement unpacking = Décompression verifying-files = Vérification des fichiers repairing-files = Réparation des fichiers +migrating-folders = Migrating folders +applying-hdiff = Applying hdiff patches +removing-outdated = Removing outdated files components-index-updated = L'index des composants a été mis à jour launch = Lancer +migrate-folders = Migrate folders +migrate-folders-tooltip = Update game folders structure apply-patch = Appliquer le patch download-wine = Télécharger wine create-prefix = Créer le préfix wine diff --git a/assets/locales/ru/errors.ftl b/assets/locales/ru/errors.ftl index aeae56b0..bc6731ad 100644 --- a/assets/locales/ru/errors.ftl +++ b/assets/locales/ru/errors.ftl @@ -6,6 +6,8 @@ debug-file-opening-error = Не удалось открыть файл отла wish-url-search-failed = Ссылка на историю молитв не найдена wish-url-opening-error = Не удалось открыть ссылку с историей молитв +wine-run-error = Не удалось запустить {$executable} используя Wine + game-launching-failed = Не удалось запустить игру failed-get-selected-wine = Не удалось найти выбранную версию Wine downloading-failed = Ошибка загрузки diff --git a/assets/locales/ru/first_run.ftl b/assets/locales/ru/first_run.ftl index 78b80fdb..760df537 100644 --- a/assets/locales/ru/first_run.ftl +++ b/assets/locales/ru/first_run.ftl @@ -38,12 +38,15 @@ show-all-folders-subtitle = Отобразить дополнительные о runners-folder = Папка версий Wine dxvks-folder = Папка версий DXVK wine-prefix-folder = Папка префикса Wine -game-installation-folder = Путь установки игры +global-game-installation-folder = Путь установки глобальной версии игры +chinese-game-installation-folder = Путь установки китайской версии игры fps-unlocker-folder = Папка FPS Unlocker components-index = Индекс компонентов patch-folder = Папка скачивания патча temp-folder = Временная папка +migrate = Перенести + select-voice-packages = Выбрать языковые пакеты diff --git a/assets/locales/ru/general.ftl b/assets/locales/ru/general.ftl index 40bb9f01..75b33f86 100644 --- a/assets/locales/ru/general.ftl +++ b/assets/locales/ru/general.ftl @@ -7,12 +7,22 @@ update-background-description = Скачивать фоновое изображ launcher-language = Язык лаунчера launcher-language-description = Применяется после перезапуска +game-edition = Редакция игры +global = Глобальная +china = Китайская + +game-environment = Окружение игры +game-environment-description = Получить особые функции такие как дополнительные методы оплаты + game-voiceovers = Язык озвучки +game-voiceovers-description = Список установленных озвучек игры. Вы можете выбрать их в настройках игры english = Английский japanese = Японский korean = Корейский chinese = Китайский +migrate-installation = Перенести лаунчер +migrate-installation-description = Открыть специальное окно в котором вы сможете перенести установленную игру repair-game = Починить игру status = Статус @@ -45,7 +55,7 @@ patch-not-applied-tooltip = Патч не применен apply-xlua-patch = Применить патч xlua ask-superuser-permissions = Запрашивать права суперпользователя -ask-superuser-permissions-description = Лаунчер будет использовать их чтобы автоматически обновлять ваш hosts файл. Это не требуется при использовании Flatpak +ask-superuser-permissions-description = Лаунчер будет использовать их чтобы автоматически обновлять ваш hosts файл. Это не требуется при использовании flatpak selected-version = Выбранная версия recommended-only = Только рекомендуемое @@ -53,6 +63,13 @@ recommended-only = Только рекомендуемое wine-version = Версия Wine wine-recommended-description = Показывать только рекомендуемые версии Wine +wine-tools = Инструменты Wine +command-line = Коммандная строка +registry-editor = Редактор реестра +explorer = Проводник +task-manager = Диспетчер задач +configuration = Настройки + dxvk-version = Версия DXVK dxvk-selection-disabled = Выбор версии DXVK отключен настройками выбранного вами Wine dxvk-recommended-description = Показывать только рекомендуемые версии DXVK diff --git a/assets/locales/ru/main.ftl b/assets/locales/ru/main.ftl index eea1e757..1cb3861b 100644 --- a/assets/locales/ru/main.ftl +++ b/assets/locales/ru/main.ftl @@ -46,12 +46,17 @@ downloading = Загрузка unpacking = Распаковка verifying-files = Проверка файлов repairing-files = Починка файлов +migrating-folders = Перемещение папок +applying-hdiff = Применение патчей hdiff +removing-outdated = Удаление устаревших файлов components-index-updated = Индекс компонентов был обновлен launch = Запустить +migrate-folders = Переместить папки +migrate-folders-tooltip = Обновить структуру файлов игры apply-patch = Применить патч download-wine = Установить Wine create-prefix = Создать префикс diff --git a/assets/locales/tr/errors.ftl b/assets/locales/tr/errors.ftl index ac99d690..de9bf315 100644 --- a/assets/locales/tr/errors.ftl +++ b/assets/locales/tr/errors.ftl @@ -6,6 +6,8 @@ debug-file-opening-error = Debug dosyasını açma başarısız oldu wish-url-search-failed = Dilekler urlsi bulunamadı wish-url-opening-error = Dilekler urlsi açılamadı +wine-run-error = Failed to run {$executable} executable using wine + game-launching-failed = Oyunu açma başarısız oldu failed-get-selected-wine = Seçilen Wine versiyonunu alma başarısız oldu downloading-failed = İndirme başarısız oldu diff --git a/assets/locales/tr/first_run.ftl b/assets/locales/tr/first_run.ftl index 25575f7c..cb61eea8 100644 --- a/assets/locales/tr/first_run.ftl +++ b/assets/locales/tr/first_run.ftl @@ -36,12 +36,15 @@ show-all-folders-subtitle = Ek yol seçimi ayarlarını göster. Dediğimi yap.. runners-folder = Başlatıcılar(Runnerlar) klasörü dxvks-folder = DXVKnın klasörü wine-prefix-folder = Wine prefixnin klasörü -game-installation-folder = Oyunun indrildiği klasör +global-game-installation-folder = Global game version installation folder +chinese-game-installation-folder = Chinese game version installation folder fps-unlocker-folder = FPS Unlocker folder components-index = Bileşenlerin dizini patch-folder = Yama klasörü temp-folder = "Geçici" klasörü +migrate = Migrate + select-voice-packages = Ses paketlerini seç diff --git a/assets/locales/tr/general.ftl b/assets/locales/tr/general.ftl index 0b4a02ba..02b3dd1e 100644 --- a/assets/locales/tr/general.ftl +++ b/assets/locales/tr/general.ftl @@ -7,12 +7,22 @@ update-background-description = İstemci için resmi arka plan resmini indirin. launcher-language = İstemci dili launcher-language-description = Restart attıktan sonra uygulanır +game-edition = Game edition +global = Global +china = China + +game-environment = Game environment +game-environment-description = Get specific features like additional payment methods + game-voiceovers = Oyun içi sesler +game-voiceovers-description = List of downloaded game voiceovers. You can select them in the game settings english = İngilizce japanese = Japonca korean = Korece chinese = Çince +migrate-installation = Migrate installation +migrate-installation-description = Open special window where you can change your game installation folder repair-game = Oyunu tamir et status = Durum @@ -53,6 +63,13 @@ recommended-only = Sadece önerilenler wine-version = Wine versiyonu wine-recommended-description = Sadece önerilen wine versiyonlarını göster +wine-tools = Wine tools +command-line = Command line +registry-editor = Registry editor +explorer = Explorer +task-manager = Task manager +configuration = Configuration + dxvk-version = DXVK versiyonu dxvk-selection-disabled = DXVK özelliği Wine grup tercihleriniz yüzünden devre dışı dxvk-recommended-description = Sadece önerilen dxvk versiyonlarını göster diff --git a/assets/locales/tr/main.ftl b/assets/locales/tr/main.ftl index 67c0e45b..0dd70c61 100644 --- a/assets/locales/tr/main.ftl +++ b/assets/locales/tr/main.ftl @@ -40,12 +40,17 @@ downloading = İndiriliyor unpacking = Paketten çıkartılıyor verifying-files = Dosyalar Doğrulanıyor repairing-files = Dosyalar tamir ediliyor +migrating-folders = Migrating folders +applying-hdiff = Applying hdiff patches +removing-outdated = Removing outdated files components-index-updated = Components index was updated launch = Çalıştır +migrate-folders = Migrate folders +migrate-folders-tooltip = Update game folders structure apply-patch = Yamayı uygula download-wine = Wine indir create-prefix = Prefix oluştur diff --git a/assets/locales/zh-cn/errors.ftl b/assets/locales/zh-cn/errors.ftl index bd415ce7..540f4953 100644 --- a/assets/locales/zh-cn/errors.ftl +++ b/assets/locales/zh-cn/errors.ftl @@ -3,8 +3,10 @@ game-folder-opening-error = 打开游戏文件夹失败 config-file-opening-error = 打开配置文件失败 debug-file-opening-error = 打开调试文件失败 -wish-url-search-failed = No wishes url found -wish-url-opening-error = Could not open wishes url +wish-url-search-failed = 找不到祈愿 URL +wish-url-opening-error = 无法转到祈愿 URL + +wine-run-error = Failed to run {$executable} executable using wine game-launching-failed = 启动游戏失败 failed-get-selected-wine = 选择 Wine 版本失败 diff --git a/assets/locales/zh-cn/first_run.ftl b/assets/locales/zh-cn/first_run.ftl index 553cfe17..8ec7824a 100644 --- a/assets/locales/zh-cn/first_run.ftl +++ b/assets/locales/zh-cn/first_run.ftl @@ -36,12 +36,15 @@ show-all-folders-subtitle = 显示额外的路径选项。按我说的做... runners-folder = 运行程序文件夹 dxvks-folder = DXVK 文件夹 wine-prefix-folder = Wine prefix 文件夹 -game-installation-folder = 游戏安装文件夹 -fps-unlocker-folder = FPS Unlocker folder +global-game-installation-folder = Global game version installation folder +chinese-game-installation-folder = Chinese game version installation folder +fps-unlocker-folder = FPS Unlocker 文件夹 components-index = 成分指数 patch-folder = 补丁文件夹 temp-folder = 临时文件夹 +migrate = Migrate + select-voice-packages = 选择语音包 diff --git a/assets/locales/zh-cn/general.ftl b/assets/locales/zh-cn/general.ftl index a3b002d5..03665f7f 100644 --- a/assets/locales/zh-cn/general.ftl +++ b/assets/locales/zh-cn/general.ftl @@ -7,12 +7,22 @@ update-background-description = 下载官方启动器背景图片。你可以关 launcher-language = 启动器语言 launcher-language-description = 重启后生效 +game-edition = Game edition +global = Global +china = China + +game-environment = Game environment +game-environment-description = Get specific features like additional payment methods + game-voiceovers = 游戏语音 +game-voiceovers-description = List of downloaded game voiceovers. You can select them in the game settings english = 英语 japanese = 日语 korean = 韩语 chinese = 汉语 +migrate-installation = Migrate installation +migrate-installation-description = Open special window where you can change your game installation folder repair-game = 修复游戏 status = 状态 @@ -24,11 +34,11 @@ game-predownload-available = 可以预下载游戏更新: {$old} -> {$new} game-update-available = 游戏版本更新: {$old} -> {$new} game-outdated = 游戏版本过旧,无法更新。最新版本: {$latest} -player-patch-version = Player patch version -player-patch-version-description = Main patch that lets you play the game on Linux +player-patch-version = 主补丁版本 +player-patch-version-description = UnitPlayer.dll 的补丁,在 Linux 上运行游戏必备 -xlua-patch-version = Xlua patch version -xlua-patch-version-description = Additional patch that fixes some issues and improves performance on low-end PCs +xlua-patch-version = xLua 补丁版本 +xlua-patch-version-description = 额外的补丁,用于修复某些问题以及改善低端 PC 的游戏性能 patch-not-available = 不可用 patch-not-available-tooltip = 无法连接补丁服务器 @@ -40,12 +50,12 @@ patch-preparation = 开发中 patch-preparation-tooltip = 补丁还在开发中 patch-testing-tooltip = 有测试版补丁可用 -patch-not-applied-tooltip = Patch is not applied +patch-not-applied-tooltip = 补丁未应用 -apply-xlua-patch = Apply xlua patch +apply-xlua-patch = 应用 xLua 补丁 -ask-superuser-permissions = Ask superuser permissions -ask-superuser-permissions-description = Launcher will use them to automatically update your hosts file. This is not needed in flatpak edition +ask-superuser-permissions = 请求超级用户权限 +ask-superuser-permissions-description = 启动器需要超级用户权限来修改 hosts 文件。flatpak 版无需此权限 selected-version = 选择版本 recommended-only = 仅显示推荐版本 @@ -53,6 +63,13 @@ recommended-only = 仅显示推荐版本 wine-version = Wine 版本 wine-recommended-description = 仅显示推荐的 Wine 版本 +wine-tools = Wine tools +command-line = Command line +registry-editor = Registry editor +explorer = Explorer +task-manager = Task manager +configuration = Configuration + dxvk-version = DXVK 版本 dxvk-selection-disabled = 您的葡萄酒组首选项禁用 DXVK 选择 dxvk-recommended-description = 仅显示推荐的 DXVK 版本 diff --git a/assets/locales/zh-cn/main.ftl b/assets/locales/zh-cn/main.ftl index 823989c6..38ea420e 100644 --- a/assets/locales/zh-cn/main.ftl +++ b/assets/locales/zh-cn/main.ftl @@ -12,7 +12,7 @@ launcher-folder = 启动器文件夹 game-folder = 游戏文件夹 config-file = 配置文件 debug-file = 调试文件 -wish-url = Open wishes +wish-url = 转到祈愿 URL about = 关于 @@ -40,12 +40,17 @@ downloading = 正在下载 unpacking = 正在解压缩 verifying-files = 正在检验文件 repairing-files = 正在修复文件 +migrating-folders = Migrating folders +applying-hdiff = Applying hdiff patches +removing-outdated = Removing outdated files -components-index-updated = Components index was updated +components-index-updated = 组件索引已更新 launch = 启动 +migrate-folders = Migrate folders +migrate-folders-tooltip = Update game folders structure apply-patch = 安装补丁 download-wine = 下载 Wine create-prefix = 创建 Wine prefix diff --git a/src/background.rs b/src/background.rs index 5c334b82..3c76e4a3 100644 --- a/src/background.rs +++ b/src/background.rs @@ -1,5 +1,5 @@ use anime_launcher_sdk::anime_game_core::installer::downloader::Downloader; -use anime_launcher_sdk::anime_game_core::curl::fetch; +use anime_launcher_sdk::anime_game_core::minreq; use md5::{Md5, Digest}; @@ -17,7 +17,7 @@ pub fn get_uri() -> String { #[cached::proc_macro::cached(result)] pub fn get_background_info() -> anyhow::Result { - let json = serde_json::from_slice::(&fetch(get_uri(), None)?.get_body()?)?; + let json = serde_json::from_slice::(minreq::get(get_uri()).send()?.as_bytes())?; let uri = match json["data"]["adv"]["background"].as_str() { Some(uri) => uri.to_owned(), @@ -60,7 +60,7 @@ pub fn download_background() -> anyhow::Result<()> { downloader.continue_downloading = false; - if let Err(err) = downloader.download_to(crate::BACKGROUND_FILE.as_path(), |_, _| {}) { + if let Err(err) = downloader.download(crate::BACKGROUND_FILE.as_path(), |_, _| {}) { anyhow::bail!(err); } diff --git a/src/main.rs b/src/main.rs index aa461563..f17433f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,9 +12,10 @@ use tracing_subscriber::filter::*; use std::path::PathBuf; +pub mod move_folder; pub mod i18n; -pub mod ui; pub mod background; +pub mod ui; use ui::main::*; use ui::first_run::main::*; @@ -37,10 +38,10 @@ lazy_static::lazy_static! { /// This one is used to prepare some launcher UI components on start pub static ref CONFIG: config::Config = config::get().expect("Failed to load config"); - pub static ref GAME: Game = Game::new(&CONFIG.game.path); + pub static ref GAME: Game = Game::new(CONFIG.game.path.for_edition(CONFIG.launcher.edition)); /// Path to launcher folder. Standard is `$HOME/.local/share/anime-game-launcher` - pub static ref LAUNCHER_FOLDER: PathBuf = launcher_dir().unwrap_or_default(); + pub static ref LAUNCHER_FOLDER: PathBuf = launcher_dir().expect("Failed to get launcher folder"); /// Path to `debug.log` file. Standard is `$HOME/.local/share/anime-game-launcher/debug.log` pub static ref DEBUG_FILE: PathBuf = LAUNCHER_FOLDER.join("debug.log"); @@ -160,7 +161,7 @@ fn main() { ", BACKGROUND_FILE.to_string_lossy())); // Set game edition - genshin::set_game_edition(CONFIG.launcher.edition.into()); + GameEdition::from(CONFIG.launcher.edition).select(); // Set UI language let lang = CONFIG.launcher.language.parse().expect("Wrong language format used in config"); diff --git a/src/move_folder.rs b/src/move_folder.rs new file mode 100644 index 00000000..c9756074 --- /dev/null +++ b/src/move_folder.rs @@ -0,0 +1,26 @@ +use std::path::Path; + +pub fn move_folder(from: &Path, to: &Path) -> std::io::Result<()> { + if !to.exists() { + std::fs::create_dir_all(to)?; + } + + for entry in from.read_dir()?.flatten() { + let to_path = to.join(entry.file_name()); + + if entry.metadata()?.is_dir() { + move_folder(&entry.path(), &to_path)?; + } + + else if entry.metadata()?.is_file() { + std::fs::copy(entry.path(), to_path)?; + std::fs::remove_file(entry.path())?; + } + + // TODO: symlinks? + } + + std::fs::remove_dir_all(from)?; + + Ok(()) +} diff --git a/src/ui/about.rs b/src/ui/about.rs index 7446fd2e..62e6fb87 100644 --- a/src/ui/about.rs +++ b/src/ui/about.rs @@ -1,18 +1,17 @@ use relm4::prelude::*; - use gtk::prelude::*; use anime_launcher_sdk::VERSION as SDK_VERSION; - -use anime_launcher_sdk::anime_game_core::{ - VERSION as CORE_VERSION, - curl_sys -}; +use anime_launcher_sdk::anime_game_core::VERSION as CORE_VERSION; use crate::*; lazy_static::lazy_static! { - static ref CURL_INFO: curl_sys::Version = curl_sys::Version::get(); + pub static ref APP_VERSION: String = if crate::APP_DEBUG && !crate::APP_VERSION.contains('-') { + format!("{}-dev", crate::APP_VERSION) + } else { + crate::APP_VERSION.to_string() + }; } #[derive(Debug)] @@ -41,17 +40,7 @@ impl SimpleComponent for AboutDialog { set_issue_url: "https://github.com/an-anime-team/an-anime-game-launcher/issues", set_license_type: gtk::License::Gpl30, - - set_version: &{ - // Debug build & build's version doesn't contain any suffix (-dev, -beta, etc) - if crate::APP_DEBUG && !crate::APP_VERSION.contains('-') { - format!("{}-dev", crate::APP_VERSION) - } - - else { - crate::APP_VERSION.to_string() - } - }, + set_version: &APP_VERSION, set_developers: &[ "Nikita Podvirnyy https://github.com/krypt0nn" @@ -81,15 +70,43 @@ impl SimpleComponent for AboutDialog { format!("Anime Launcher SDK: {SDK_VERSION}"), format!("Anime Game Core: {CORE_VERSION}"), String::new(), - format!("curl: {}", CURL_INFO.version()), - format!("SSL: {}", CURL_INFO.ssl_version().unwrap_or("?")), - String::new(), format!("GTK: {}.{}.{}", gtk::major_version(), gtk::minor_version(), gtk::micro_version()), format!("libadwaita: {}.{}.{}", adw::major_version(), adw::minor_version(), adw::micro_version()), format!("pango: {}", gtk::pango::version_string()), format!("cairo: {}", gtk::cairo::version_string()), ].join("\n"), + set_release_notes_version: &APP_VERSION, + set_release_notes: &[ + "

Added

", + + "
    ", + "
  • Added installation migration feature
  • ", + "
  • Added game environment switcher
  • ", + "
  • Added game edition switcher
  • ", + "
  • Added changelog to updated components toast
  • ", + "
  • Added wine tools to settings
  • ", + "
  • Added preferences search
  • ", + "
  • Added new progress bar statuses for applyign hdiff patches and removing outdated files
  • ", + "
  • Added automatic 3.5 -> 3.6 voiceover files migration related to changed files structure
  • ", + "
", + + "

Fixed

", + + "
    ", + "
  • Added whitespaces removing from environment values
  • ", + "
", + + "

Changed

", + + "
    ", + "
  • Improved game repairing feature
  • ", + "
  • Replaced curl dependency by native code
  • ", + "
  • Replaced static image by spinner in wine / dxvk version selection
  • ", + "
  • Made wine / dxvk versions always visible if they're downloaded
  • ", + "
", + ].join("\n"), + set_modal: true, set_hide_on_close: true, diff --git a/src/ui/components/mod.rs b/src/ui/components/mod.rs index 8daae2fb..2e81eac0 100644 --- a/src/ui/components/mod.rs +++ b/src/ui/components/mod.rs @@ -25,6 +25,7 @@ pub struct ComponentsListGroup { } impl From for ComponentsListGroup { + #[inline] fn from(group: wine::Group) -> Self { Self { title: group.title, @@ -34,6 +35,7 @@ impl From for ComponentsListGroup { } impl From for ComponentsListGroup { + #[inline] fn from(group: dxvk::Group) -> Self { Self { title: group.title, @@ -51,23 +53,33 @@ pub struct ComponentsListVersion { } impl From for ComponentsListVersion { + #[inline] fn from(version: wine::Version) -> Self { Self { + recommended: match version.version_features() { + Some(features) => features.recommended, + None => true + }, + name: version.name, title: version.title, - uri: version.uri, - recommended: true // FIXME + uri: version.uri } } } impl From for ComponentsListVersion { + #[inline] fn from(version: dxvk::Version) -> Self { Self { - name: version.name.clone(), - title: version.name, - uri: version.uri, - recommended: true // FIXME + recommended: match version.version_features() { + Some(features) => features.recommended, + None => true + }, + + name: version.name, + title: version.title, + uri: version.uri } } } diff --git a/src/ui/components/progress_bar.rs b/src/ui/components/progress_bar.rs index 2d86ddfa..681e0289 100644 --- a/src/ui/components/progress_bar.rs +++ b/src/ui/components/progress_bar.rs @@ -3,8 +3,7 @@ use relm4::component::*; use adw::prelude::*; -use anime_launcher_sdk::anime_game_core::installer::installer::Update as InstallerUpdate; -use anime_launcher_sdk::anime_game_core::prettify_bytes::prettify_bytes; +use anime_launcher_sdk::anime_game_core::prelude::*; use crate::i18n::*; @@ -44,7 +43,7 @@ pub enum ProgressBarMsg { /// (current bytes, total bytes) UpdateProgress(u64, u64), - UpdateFromState(InstallerUpdate), + UpdateFromState(DiffUpdate), SetVisible(bool) } @@ -128,12 +127,36 @@ impl SimpleAsyncComponent for ProgressBar { ProgressBarMsg::UpdateFromState(state) => { match state { - InstallerUpdate::CheckingFreeSpace(_) => self.caption = Some(tr("checking-free-space")), - InstallerUpdate::DownloadingStarted(_) => self.caption = Some(tr("downloading")), - InstallerUpdate::UnpackingStarted(_) => self.caption = Some(tr("unpacking")), + DiffUpdate::InstallerUpdate(InstallerUpdate::CheckingFreeSpace(_)) => self.caption = Some(tr("checking-free-space")), - InstallerUpdate::DownloadingProgress(curr, total) | - InstallerUpdate::UnpackingProgress(curr, total) => { + DiffUpdate::InstallerUpdate(InstallerUpdate::DownloadingStarted(_)) => { + self.caption = Some(tr("downloading")); + + self.display_fraction = true; + } + + DiffUpdate::InstallerUpdate(InstallerUpdate::UnpackingStarted(_)) => { + self.caption = Some(tr("unpacking")); + + self.display_fraction = true; + } + + DiffUpdate::ApplyingHdiffStarted => { + self.caption = Some(tr("applying-hdiff")); + + self.display_fraction = false; + } + + DiffUpdate::RemovingOutdatedStarted => { + self.caption = Some(tr("removing-outdated")); + + self.display_fraction = false; + } + + DiffUpdate::InstallerUpdate(InstallerUpdate::DownloadingProgress(curr, total)) | + DiffUpdate::InstallerUpdate(InstallerUpdate::UnpackingProgress(curr, total)) | + DiffUpdate::ApplyingHdiffProgress(curr, total) | + DiffUpdate::RemovingOutdatedProgress(curr, total) => { self.fraction = curr as f64 / total as f64; self.downloaded = Some(( @@ -142,11 +165,14 @@ impl SimpleAsyncComponent for ProgressBar { )); } - InstallerUpdate::DownloadingFinished => tracing::info!("Downloading finished"), - InstallerUpdate::UnpackingFinished => tracing::info!("Unpacking finished"), + DiffUpdate::InstallerUpdate(InstallerUpdate::DownloadingFinished) => tracing::info!("Downloading finished"), + DiffUpdate::InstallerUpdate(InstallerUpdate::UnpackingFinished) => tracing::info!("Unpacking finished"), + + DiffUpdate::ApplyingHdiffFinished => tracing::info!("Applying hdiffs finished"), + DiffUpdate::RemovingOutdatedFinished => tracing::info!("Removing outdated files finished"), - InstallerUpdate::DownloadingError(err) => tracing::error!("Downloading error: {:?}", err), - InstallerUpdate::UnpackingError(err) => tracing::error!("Unpacking error: {:?}", err) + DiffUpdate::InstallerUpdate(InstallerUpdate::DownloadingError(err)) => tracing::error!("Downloading error: {:?}", err), + DiffUpdate::InstallerUpdate(InstallerUpdate::UnpackingError(err)) => tracing::error!("Unpacking error: {:?}", err) } } diff --git a/src/ui/components/version.rs b/src/ui/components/version.rs index c794daba..5f10ca3f 100644 --- a/src/ui/components/version.rs +++ b/src/ui/components/version.rs @@ -9,7 +9,7 @@ use gtk::glib::clone; use super::progress_bar::ProgressBarMsg; use anime_launcher_sdk::config; -use anime_launcher_sdk::anime_game_core::installer::installer::*; +use anime_launcher_sdk::anime_game_core::prelude::*; use std::path::PathBuf; @@ -52,7 +52,7 @@ impl SimpleAsyncComponent for ComponentVersion { set_title: &model.title, #[watch] - set_visible: !model.show_recommended_only || model.recommended, + set_visible: !model.show_recommended_only || model.recommended || model.state != VersionState::NotDownloaded, add_suffix = >k::Button { #[watch] @@ -162,12 +162,12 @@ impl SimpleAsyncComponent for ComponentVersion { installer.install(download_folder, move |state| { match &state { - Update::UnpackingFinished | - Update::DownloadingError(_) | - Update::UnpackingError(_) => { + InstallerUpdate::UnpackingFinished | + InstallerUpdate::DownloadingError(_) | + InstallerUpdate::UnpackingError(_) => { progress_bar_sender.send(ProgressBarMsg::SetVisible(false)); - if let Update::UnpackingFinished = &state { + if let InstallerUpdate::UnpackingFinished = &state { sender.input(AppMsg::SetState(VersionState::Downloaded)); sender.output(super::group::AppMsg::CallOnDownloaded); } @@ -180,7 +180,7 @@ impl SimpleAsyncComponent for ComponentVersion { _ => () } - progress_bar_sender.send(ProgressBarMsg::UpdateFromState(state)); + progress_bar_sender.send(ProgressBarMsg::UpdateFromState(DiffUpdate::InstallerUpdate(state))); }); })); } diff --git a/src/ui/first_run/default_paths.rs b/src/ui/first_run/default_paths.rs index 3c5d7a61..ca78482f 100644 --- a/src/ui/first_run/default_paths.rs +++ b/src/ui/first_run/default_paths.rs @@ -10,15 +10,21 @@ use std::path::PathBuf; use crate::*; use crate::i18n::*; use super::main::*; +use crate::ui::components::progress_bar::*; pub struct DefaultPathsApp { + progress_bar: AsyncController, + show_additional: bool, + migrate_installation: bool, + show_progress: bool, launcher: PathBuf, runners: PathBuf, dxvks: PathBuf, prefix: PathBuf, - game: PathBuf, + game_global: PathBuf, + game_china: PathBuf, fps_unlocker: PathBuf, components: PathBuf, patch: PathBuf, @@ -31,7 +37,8 @@ pub enum Folders { Runners, DXVK, Prefix, - Game, + GameGlobal, + GameChina, FpsUnlocker, Components, Patch, @@ -48,7 +55,8 @@ pub enum DefaultPathsAppMsg { #[relm4::component(async, pub)] impl SimpleAsyncComponent for DefaultPathsApp { - type Init = (); + /// If `true`, then use migrate installation mode + type Init = bool; type Input = DefaultPathsAppMsg; type Output = FirstRunAppMsg; @@ -70,6 +78,9 @@ impl SimpleAsyncComponent for DefaultPathsApp { set_valign: gtk::Align::End, set_vexpand: true, + #[watch] + set_sensitive: !model.show_progress, + adw::ActionRow { set_title: &tr("launcher-folder"), set_icon_name: Some("folder-symbolic"), @@ -105,6 +116,9 @@ impl SimpleAsyncComponent for DefaultPathsApp { #[watch] set_visible: model.show_additional, + #[watch] + set_sensitive: !model.show_progress, + adw::ActionRow { set_title: &tr("runners-folder"), set_icon_name: Some("folder-symbolic"), @@ -139,14 +153,25 @@ impl SimpleAsyncComponent for DefaultPathsApp { }, adw::ActionRow { - set_title: &tr("game-installation-folder"), + set_title: &tr("global-game-installation-folder"), + set_icon_name: Some("folder-symbolic"), + set_activatable: true, + + #[watch] + set_subtitle: model.game_global.to_str().unwrap(), + + connect_activated => DefaultPathsAppMsg::ChoosePath(Folders::GameGlobal) + }, + + adw::ActionRow { + set_title: &tr("chinese-game-installation-folder"), set_icon_name: Some("folder-symbolic"), set_activatable: true, #[watch] - set_subtitle: model.game.to_str().unwrap(), + set_subtitle: model.game_china.to_str().unwrap(), - connect_activated => DefaultPathsAppMsg::ChoosePath(Folders::Game) + connect_activated => DefaultPathsAppMsg::ChoosePath(Folders::GameChina) }, adw::ActionRow { @@ -197,43 +222,86 @@ impl SimpleAsyncComponent for DefaultPathsApp { add = &adw::PreferencesGroup { set_valign: gtk::Align::Center, set_vexpand: true, - + + #[watch] + set_visible: !model.show_progress, + gtk::Box { set_orientation: gtk::Orientation::Horizontal, set_halign: gtk::Align::Center, set_spacing: 8, - + gtk::Button { - set_label: &tr("continue"), + set_label: &if model.migrate_installation { + tr("migrate") + } else { + tr("continue") + }, + set_css_classes: &["suggested-action", "pill"], connect_clicked => DefaultPathsAppMsg::Continue }, gtk::Button { - set_label: &tr("exit"), + set_label: &if model.migrate_installation { + tr("close") + } else { + tr("exit") + }, + add_css_class: "pill", + #[watch] + set_visible: !model.migrate_installation, + connect_clicked => DefaultPathsAppMsg::Exit } } + }, + + add = &adw::PreferencesGroup { + set_valign: gtk::Align::Center, + set_vexpand: true, + + #[watch] + set_visible: model.show_progress, + + gtk::Box { + set_orientation: gtk::Orientation::Horizontal, + set_halign: gtk::Align::Center, + + append = model.progress_bar.widget(), + } } } } async fn init( - _init: Self::Init, + init: Self::Init, root: Self::Root, _sender: AsyncComponentSender, ) -> AsyncComponentParts { let model = Self { + progress_bar: ProgressBar::builder() + .launch(ProgressBarInit { + caption: None, + display_progress: true, + display_fraction: false, + visible: false + }) + .detach(), + show_additional: false, + migrate_installation: init, + show_progress: false, launcher: LAUNCHER_FOLDER.to_path_buf(), runners: CONFIG.game.wine.builds.clone(), dxvks: CONFIG.game.dxvk.builds.clone(), prefix: CONFIG.game.wine.prefix.clone(), - game: CONFIG.game.path.clone(), + game_global: CONFIG.game.path.global.clone(), + game_china: CONFIG.game.path.china.clone(), fps_unlocker: CONFIG.game.enhancements.fps_unlocker.path.clone(), components: CONFIG.components.path.clone(), patch: CONFIG.patch.path.clone(), @@ -242,6 +310,9 @@ impl SimpleAsyncComponent for DefaultPathsApp { temp: CONFIG.launcher.temp.clone().unwrap_or(std::env::temp_dir()) }; + // Set progress bar width + model.progress_bar.widget().set_width_request(400); + let widgets = view_output!(); AsyncComponentParts { model, widgets } @@ -264,7 +335,8 @@ impl SimpleAsyncComponent for DefaultPathsApp { self.runners = result.join("runners"); self.dxvks = result.join("dxvks"); self.prefix = result.join("prefix"); - self.game = result.join("Genshin Impact"); // TODO: change it based on GameEdition + self.game_global = result.join(concat!("Ge", "nshi", "n Imp", "act")); + self.game_china = result.join(concat!("Yu", "anS", "hen")); self.fps_unlocker = result.join("fps-unlocker"); self.components = result.join("components"); self.patch = result.join("patch"); @@ -276,7 +348,8 @@ impl SimpleAsyncComponent for DefaultPathsApp { Folders::Runners => self.runners = result, Folders::DXVK => self.dxvks = result, Folders::Prefix => self.prefix = result, - Folders::Game => self.game = result, + Folders::GameGlobal => self.game_global = result, + Folders::GameChina => self.game_china = result, Folders::FpsUnlocker => self.fps_unlocker = result, Folders::Components => self.components = result, Folders::Patch => self.patch = result, @@ -287,17 +360,71 @@ impl SimpleAsyncComponent for DefaultPathsApp { #[allow(unused_must_use)] DefaultPathsAppMsg::Continue => { + let old_config = config::get().unwrap_or_else(|_| CONFIG.clone()); + match self.update_config() { - Ok(_) => sender.output(Self::Output::ScrollToSelectVoiceovers), - - Err(err) => sender.output(Self::Output::Toast { - title: tr("config-update-error"), - description: Some(err.to_string()) - }) - }; + Ok(_) => { + if self.migrate_installation { + self.progress_bar.sender().send(ProgressBarMsg::SetVisible(true)); + + self.show_progress = true; + + let folders = [ + (old_config.game.wine.builds, &self.runners), + (old_config.game.dxvk.builds, &self.dxvks), + (old_config.game.wine.prefix, &self.prefix), + (old_config.game.path.global, &self.game_global), + (old_config.game.path.china, &self.game_china), + (old_config.components.path, &self.components), + (old_config.patch.path, &self.patch), + + (old_config.game.enhancements.fps_unlocker.path, &self.fps_unlocker) + ]; + + #[allow(clippy::expect_fun_call)] + for (i, (from, to)) in folders.iter().enumerate() { + self.progress_bar.sender().send(ProgressBarMsg::UpdateCaption(Some( + from.to_str().map(|str| str.to_string()).unwrap_or_else(|| format!("{:?}", from)) + ))); + + if &from != to && from.exists() { + move_folder::move_folder(from, to).expect(&format!("Failed to move folder: {:?} -> {:?}", from, to)); + } + + self.progress_bar.sender().send(ProgressBarMsg::UpdateProgress(i as u64 + 1, folders.len() as u64)); + } + + // Restart the app + + std::process::Command::new(std::env::current_exe().unwrap()).spawn().unwrap(); + + relm4::main_application().quit(); + } + + else { + sender.output(Self::Output::ScrollToSelectVoiceovers); + } + } + + Err(err) => { + sender.output(Self::Output::Toast { + title: tr("config-update-error"), + description: Some(err.to_string()) + }); + } + } } - DefaultPathsAppMsg::Exit => relm4::main_application().quit() + DefaultPathsAppMsg::Exit => { + if self.migrate_installation { + // TODO: this shit should return message to general preferences component somehow to close MigrateInstallation window + todo!(); + } + + else { + relm4::main_application().quit(); + } + } } } } @@ -309,7 +436,8 @@ impl DefaultPathsApp { config.game.wine.builds = self.runners.clone(); config.game.dxvk.builds = self.dxvks.clone(); config.game.wine.prefix = self.prefix.clone(); - config.game.path = self.game.clone(); + config.game.path.global = self.game_global.clone(); + config.game.path.china = self.game_china.clone(); config.components.path = self.components.clone(); config.patch.path = self.patch.clone(); config.launcher.temp = Some(self.temp.clone()); diff --git a/src/ui/first_run/download_components.rs b/src/ui/first_run/download_components.rs index 6d187011..987e6e6f 100644 --- a/src/ui/first_run/download_components.rs +++ b/src/ui/first_run/download_components.rs @@ -3,9 +3,9 @@ use relm4::component::*; use adw::prelude::*; +use anime_launcher_sdk::anime_game_core::prelude::*; use anime_launcher_sdk::components::*; use anime_launcher_sdk::components::wine::WincompatlibWine; -use anime_launcher_sdk::anime_game_core::installer::prelude::*; use anime_launcher_sdk::config; use anime_launcher_sdk::wincompatlib::prelude::*; @@ -421,7 +421,7 @@ impl SimpleAsyncComponent for DownloadComponentsApp { _ => () } - progress_bar_input.send(ProgressBarMsg::UpdateFromState(update)); + progress_bar_input.send(ProgressBarMsg::UpdateFromState(DiffUpdate::InstallerUpdate(update))); }); } @@ -532,7 +532,7 @@ impl SimpleAsyncComponent for DownloadComponentsApp { _ => () } - progress_bar_input.send(ProgressBarMsg::UpdateFromState(update)); + progress_bar_input.send(ProgressBarMsg::UpdateFromState(DiffUpdate::InstallerUpdate(update))); }); } diff --git a/src/ui/first_run/main.rs b/src/ui/first_run/main.rs index c2d0fc25..43b8b9f1 100644 --- a/src/ui/first_run/main.rs +++ b/src/ui/first_run/main.rs @@ -17,7 +17,7 @@ use super::select_voiceovers::*; use super::download_components::*; use super::finish::*; -pub static mut MAIN_WINDOW: Option = None; +pub static mut MAIN_WINDOW: Option = None; // TODO: add special page for launcher style selection @@ -61,7 +61,7 @@ impl SimpleComponent for FirstRunApp { type Output = (); view! { - window = adw::Window { + window = adw::ApplicationWindow { set_default_size: (780, 560), #[watch] @@ -145,7 +145,7 @@ impl SimpleComponent for FirstRunApp { .forward(sender.input_sender(), std::convert::identity), default_paths: DefaultPathsApp::builder() - .launch(()) + .launch(false) .forward(sender.input_sender(), std::convert::identity), select_voiceovers: SelectVoiceoversApp::builder() @@ -232,8 +232,7 @@ impl SimpleComponent for FirstRunApp { Ok(None) => { for host in &CONFIG.components.servers { match components.sync(host) { - Ok(true) => break, - Ok(false) => continue, + Ok(_) => break, Err(err) => { tracing::error!("Failed to sync components index"); diff --git a/src/ui/main/apply_patch.rs b/src/ui/main/apply_patch.rs index 6cecf8c9..794a3bed 100644 --- a/src/ui/main/apply_patch.rs +++ b/src/ui/main/apply_patch.rs @@ -21,7 +21,7 @@ pub fn apply_patch(sender: ComponentSender< std::thread::spawn(move || { let mut apply_patch_if_needed = true; - if let Err(err) = patch.apply(&config.game.path, config.patch.root) { + if let Err(err) = patch.apply(config.game.path.for_edition(config.launcher.edition), config.patch.root) { tracing::error!("Failed to patch the game"); sender.input(AppMsg::Toast { diff --git a/src/ui/main/download_diff.rs b/src/ui/main/download_diff.rs index 5aa88590..c35d4d3b 100644 --- a/src/ui/main/download_diff.rs +++ b/src/ui/main/download_diff.rs @@ -18,11 +18,11 @@ pub fn download_diff(sender: ComponentSender, progress_bar_input: Sender move |state| { + let result = diff.install_to_by(game_path, config.launcher.temp, clone!(@strong sender => move |state| { match &state { - InstallerUpdate::DownloadingError(err) => { + DiffUpdate::InstallerUpdate(InstallerUpdate::DownloadingError(err)) => { tracing::error!("Downloading failed: {err}"); sender.input(AppMsg::Toast { @@ -31,7 +31,7 @@ pub fn download_diff(sender: ComponentSender, progress_bar_input: Sender { + DiffUpdate::InstallerUpdate(InstallerUpdate::UnpackingError(err)) => { tracing::error!("Unpacking failed: {err}"); sender.input(AppMsg::Toast { @@ -43,7 +43,9 @@ pub fn download_diff(sender: ComponentSender, progress_bar_input: Sender () } - progress_bar_input.send(ProgressBarMsg::UpdateFromState(state)); + #[allow(unused_must_use)] { + progress_bar_input.send(ProgressBarMsg::UpdateFromState(state)); + } })); let mut perform_on_download_needed = true; diff --git a/src/ui/main/download_wine.rs b/src/ui/main/download_wine.rs index d8137645..d33ea3c8 100644 --- a/src/ui/main/download_wine.rs +++ b/src/ui/main/download_wine.rs @@ -55,7 +55,6 @@ pub fn download_wine(sender: ComponentSender, progress_bar_input: Sender move || { - #[allow(unused_must_use)] installer.install(&config.game.wine.builds, clone!(@strong sender => move |state| { match &state { InstallerUpdate::DownloadingError(err) => { @@ -79,7 +78,9 @@ pub fn download_wine(sender: ComponentSender, progress_bar_input: Sender () } - progress_bar_input.send(ProgressBarMsg::UpdateFromState(state)); + #[allow(unused_must_use)] { + progress_bar_input.send(ProgressBarMsg::UpdateFromState(DiffUpdate::InstallerUpdate(state))); + } })); config.game.wine.selected = Some(wine.name.clone()); diff --git a/src/ui/main/migrate_folder.rs b/src/ui/main/migrate_folder.rs new file mode 100644 index 00000000..74d6b61f --- /dev/null +++ b/src/ui/main/migrate_folder.rs @@ -0,0 +1,25 @@ +use relm4::prelude::*; + +use std::path::PathBuf; + +use crate::*; +use super::{App, AppMsg}; + +pub fn migrate_folder(sender: ComponentSender, from: PathBuf, to: PathBuf, cleanup_folder: Option) { + sender.input(AppMsg::DisableButtons(true)); + + std::thread::spawn(move || { + move_folder::move_folder(&from, &to).expect("Failed to perform migration"); + + if let Some(cleanup_folder) = cleanup_folder { + std::fs::remove_dir_all(cleanup_folder).expect("Failed to remove cleanup folder"); + } + + sender.input(AppMsg::DisableButtons(false)); + sender.input(AppMsg::UpdateLauncherState { + perform_on_download_needed: false, + apply_patch_if_needed: false, + show_status_page: true + }); + }); +} diff --git a/src/ui/main/mod.rs b/src/ui/main/mod.rs index 174090d1..0ed377dc 100644 --- a/src/ui/main/mod.rs +++ b/src/ui/main/mod.rs @@ -15,11 +15,13 @@ mod apply_patch; mod download_wine; mod create_prefix; mod download_diff; +mod migrate_folder; mod launch; use anime_launcher_sdk::config::launcher::LauncherStyle; use anime_launcher_sdk::states::LauncherState; use anime_launcher_sdk::components::loader::ComponentsLoader; +use anime_launcher_sdk::anime_game_core::genshin::consts::GameEdition; use crate::*; use crate::i18n::*; @@ -38,9 +40,9 @@ relm4::new_stateless_action!(WishUrl, WindowActionGroup, "wish_url"); relm4::new_stateless_action!(About, WindowActionGroup, "about"); -static mut MAIN_WINDOW: Option = None; -static mut PREFERENCES_WINDOW: Option> = None; -static mut ABOUT_DIALOG: Option> = None; +pub static mut MAIN_WINDOW: Option = None; +pub static mut PREFERENCES_WINDOW: Option> = None; +pub static mut ABOUT_DIALOG: Option> = None; pub struct App { progress_bar: AsyncController, @@ -370,24 +372,25 @@ impl SimpleComponent for App { gtk::Button { #[watch] set_label: &match model.state { - Some(LauncherState::Launch) => tr("launch"), - Some(LauncherState::PredownloadAvailable { .. }) => tr("launch"), - Some(LauncherState::UnityPlayerPatchAvailable(_)) => tr("apply-patch"), - Some(LauncherState::XluaPatchAvailable(_)) => tr("apply-patch"), - Some(LauncherState::WineNotInstalled) => tr("download-wine"), - Some(LauncherState::PrefixNotExists) => tr("create-prefix"), - Some(LauncherState::VoiceUpdateAvailable(_)) => tr("update"), - Some(LauncherState::VoiceOutdated(_)) => tr("update"), - Some(LauncherState::VoiceNotInstalled(_)) => tr("download"), - Some(LauncherState::GameUpdateAvailable(_)) => tr("update"), - Some(LauncherState::GameOutdated(_)) => tr("update"), - Some(LauncherState::GameNotInstalled(_)) => tr("download"), + Some(LauncherState::Launch) => tr("launch"), + Some(LauncherState::PredownloadAvailable { .. }) => tr("launch"), + Some(LauncherState::FolderMigrationRequired { .. }) => tr("migrate-folders"), + Some(LauncherState::UnityPlayerPatchAvailable(_)) => tr("apply-patch"), + Some(LauncherState::XluaPatchAvailable(_)) => tr("apply-patch"), + Some(LauncherState::WineNotInstalled) => tr("download-wine"), + Some(LauncherState::PrefixNotExists) => tr("create-prefix"), + Some(LauncherState::VoiceUpdateAvailable(_)) => tr("update"), + Some(LauncherState::VoiceOutdated(_)) => tr("update"), + Some(LauncherState::VoiceNotInstalled(_)) => tr("download"), + Some(LauncherState::GameUpdateAvailable(_)) => tr("update"), + Some(LauncherState::GameOutdated(_)) => tr("update"), + Some(LauncherState::GameNotInstalled(_)) => tr("download"), None => String::from("...") }, #[watch] - set_sensitive: match model.state.as_ref() { + set_sensitive: !model.disabled_buttons && match &model.state { Some(LauncherState::GameOutdated { .. }) | Some(LauncherState::VoiceOutdated(_)) => false, @@ -407,7 +410,7 @@ impl SimpleComponent for App { }, #[watch] - set_css_classes: match model.state.as_ref() { + set_css_classes: match &model.state { Some(LauncherState::GameOutdated { .. }) | Some(LauncherState::VoiceOutdated(_)) => &["warning"], @@ -427,10 +430,12 @@ impl SimpleComponent for App { }, #[watch] - set_tooltip_text: Some(&match model.state.as_ref() { + set_tooltip_text: Some(&match &model.state { Some(LauncherState::GameOutdated { .. }) | Some(LauncherState::VoiceOutdated(_)) => tr("main-window--version-outdated-tooltip"), + Some(LauncherState::FolderMigrationRequired { .. }) => tr("migrate-folders-tooltip"), + Some(LauncherState::UnityPlayerPatchAvailable(UnityPlayerPatch { status, .. })) | Some(LauncherState::XluaPatchAvailable(XluaPatch { status, .. })) => match status { PatchStatus::NotAvailable => tr("main-window--patch-unavailable-tooltip"), @@ -444,9 +449,6 @@ impl SimpleComponent for App { _ => String::new() }), - #[watch] - set_sensitive: !model.disabled_buttons, - set_hexpand: false, set_width_request: 200, @@ -556,7 +558,12 @@ impl SimpleComponent for App { }))); group.add_action::(&RelmAction::new_stateless(clone!(@strong sender => move |_| { - if let Err(err) = open::that(&CONFIG.game.path) { + let path = match config::get() { + Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), + Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(), + }; + + if let Err(err) = open::that(path) { sender.input(AppMsg::Toast { title: tr("game-folder-opening-error"), description: Some(err.to_string()) @@ -567,7 +574,7 @@ impl SimpleComponent for App { }))); group.add_action::(&RelmAction::new_stateless(clone!(@strong sender => move |_| { - if let Some(file) = anime_launcher_sdk::consts::config_file() { + if let Ok(file) = anime_launcher_sdk::consts::config_file() { if let Err(err) = open::that(file) { sender.input(AppMsg::Toast { title: tr("config-file-opening-error"), @@ -592,8 +599,10 @@ impl SimpleComponent for App { group.add_action::(&RelmAction::new_stateless(clone!(@strong sender => move |_| { std::thread::spawn(clone!(@strong sender => move || { - let web_cache = CONFIG.game.path - .join(unsafe { anime_launcher_sdk::anime_game_core::genshin::consts::DATA_FOLDER_NAME }) + let config = config::get().unwrap_or_else(|_| CONFIG.clone()); + + let web_cache = config.game.path.for_edition(config.launcher.edition) + .join(GameEdition::from(config.launcher.edition).data_folder()) .join("webCaches/Cache/Cache_Data/data_2"); if !web_cache.exists() { @@ -668,7 +677,7 @@ impl SimpleComponent for App { sender.input(AppMsg::SetLoadingStatus(Some(Some(tr("downloading-background-picture"))))); if let Err(err) = crate::background::download_background() { - tracing::error!("Failed to download background picture"); + tracing::error!("Failed to download background picture: {err}"); sender.input(AppMsg::Toast { title: tr("background-downloading-failed"), @@ -689,19 +698,24 @@ impl SimpleComponent for App { Ok(None) => { for host in &CONFIG.components.servers { match components.sync(host) { - Ok(true) => { - // TODO: add changelog log here - + Ok(changes) => { sender.input(AppMsg::Toast { title: tr("components-index-updated"), - description: None + description: if changes.is_empty() { + None + } else { + let max_len = changes.iter().map(|line| line.len()).max().unwrap_or(80); + + Some(changes.into_iter() + .map(|line| format!("- {line}{}", " ".repeat(max_len - line.len()))) + .collect::>() + .join("\n")) + } }); break; } - Ok(false) => continue, - Err(err) => { tracing::error!("Failed to sync components index"); @@ -737,16 +751,7 @@ impl SimpleComponent for App { Ok(None) => { for server in &CONFIG.patch.servers { match patch.sync(server) { - Ok(true) => break, - - Ok(false) => { - tracing::error!("Failed to sync patch folder with remote: {server}"); - - sender.input(AppMsg::Toast { - title: tr("patch-sync-failed"), - description: None - }); - } + Ok(_) => break, Err(err) => { tracing::error!("Failed to sync patch folder with remote: {server}: {err}"); @@ -1005,6 +1010,9 @@ impl SimpleComponent for App { LauncherState::PredownloadAvailable { .. } | LauncherState::Launch => launch::launch(sender), + LauncherState::FolderMigrationRequired { from, to, cleanup_folder } => + migrate_folder::migrate_folder(sender, from.to_owned(), to.to_owned(), cleanup_folder.to_owned()), + LauncherState::UnityPlayerPatchAvailable(patch) => apply_patch::apply_patch(sender, patch.to_owned()), LauncherState::XluaPatchAvailable(patch) => apply_patch::apply_patch(sender, patch.to_owned()), @@ -1015,7 +1023,8 @@ impl SimpleComponent for App { LauncherState::VoiceUpdateAvailable(diff) | LauncherState::VoiceNotInstalled(diff) | LauncherState::GameUpdateAvailable(diff) | - LauncherState::GameNotInstalled(diff) => download_diff::download_diff(sender, self.progress_bar.sender().to_owned(), diff.to_owned()), + LauncherState::GameNotInstalled(diff) => + download_diff::download_diff(sender, self.progress_bar.sender().to_owned(), diff.to_owned()), LauncherState::VoiceOutdated(_) | LauncherState::GameOutdated(_) => () diff --git a/src/ui/main/repair_game.rs b/src/ui/main/repair_game.rs index d48719b9..243c699b 100644 --- a/src/ui/main/repair_game.rs +++ b/src/ui/main/repair_game.rs @@ -3,6 +3,8 @@ use relm4::{ Sender }; +use gtk::glib::clone; + use std::path::Path; use anime_launcher_sdk::config; @@ -23,7 +25,8 @@ pub fn repair_game(sender: ComponentSender, progress_bar_input: Sender { // Add voiceovers files - let game = Game::new(&config.game.path); + let game_path = config.game.path.for_edition(config.launcher.edition).to_path_buf(); + let game = Game::new(&game_path); if let Ok(voiceovers) = game.get_voice_packages() { for package in voiceovers { @@ -61,10 +64,9 @@ pub fn repair_game(sender: ComponentSender, progress_bar_input: Sender move || { for file in thread_files { let status = if config.launcher.repairer.fast { file.fast_verify(&game_path) @@ -74,7 +76,7 @@ pub fn repair_game(sender: ComponentSender, progress_bar_input: Sender, progress_bar_input: Sender bool { - for part in ["UnityPlayer.dll", "xlua.dll", "crashreport.exe", "upload_crash.exe", "vulkan-1.dll"] { + fn should_ignore(path: &Path, player_patch: bool, xlua_patch: bool) -> bool { + // Files managed by launch.bat file + for part in ["crashreport.exe", "upload_crash.exe"] { if path.ends_with(part) { return true; } } + // UnityPlayer patch related files + if player_patch { + for part in ["UnityPlayer.dll", "vulkan-1.dll"] { + if path.ends_with(part) { + return true; + } + } + } + + // Xlua patch related files + if xlua_patch { + for part in ["xlua.dll", "mhypbase.dll"] { + if path.ends_with(part) { + return true; + } + } + } + false } for (i, file) in broken.into_iter().enumerate() { - if !is_patch_applied || !should_ignore(&file.path) { - tracing::debug!("Repairing: {}", file.path.to_string_lossy()); + if !should_ignore(&file.path, player_patch, xlua_patch) { + tracing::debug!("Repairing file: {}", file.path.to_string_lossy()); - if let Err(err) = file.repair(&config.game.path) { + if let Err(err) = file.repair(&game_path) { sender.input(AppMsg::Toast { title: tr("game-file-repairing-error"), description: Some(err.to_string()) @@ -134,6 +157,10 @@ pub fn repair_game(sender: ComponentSender, progress_bar_input: Sender, +} + +#[relm4::component(pub)] +impl SimpleComponent for MigrateInstallationApp { + type Init = (); + type Input = (); + type Output = (); + + view! { + adw::Window { + set_default_size: (780, 560), + set_modal: true, + set_hide_on_close: true, + + #[watch] + set_title: Some(&tr("migrate-installation")), + + gtk::Box { + set_orientation: gtk::Orientation::Vertical, + + adw::HeaderBar { + add_css_class: "flat" + }, + + append = model.default_paths.widget(), + } + } + } + + fn init( + _init: Self::Init, + root: &Self::Root, + _sender: ComponentSender, + ) -> ComponentParts { + tracing::info!("Initializing migration window"); + + let model = Self { + default_paths: DefaultPathsApp::builder() + .launch(true) + .detach() + }; + + let widgets = view_output!(); + + ComponentParts { model, widgets } + } +} diff --git a/src/ui/mod.rs b/src/ui/mod.rs index e5365c9a..eae65455 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -3,3 +3,4 @@ pub mod about; pub mod preferences; pub mod components; pub mod first_run; +pub mod migrate_installation; diff --git a/src/ui/preferences/environment.rs b/src/ui/preferences/environment.rs index 19d168d0..e94d7bd1 100644 --- a/src/ui/preferences/environment.rs +++ b/src/ui/preferences/environment.rs @@ -91,11 +91,11 @@ impl SimpleAsyncComponent for EnvironmentApp { adw::EntryRow { set_title: "%command%", - set_text: CONFIG.game.command.as_ref().unwrap_or(&String::new()), + set_text: CONFIG.game.command.as_ref().unwrap_or(&String::new()).trim(), connect_changed => |entry| { if let Ok(mut config) = config::get() { - let command = entry.text().to_string(); + let command = entry.text().trim().to_string(); config.game.command = if command.is_empty() { None @@ -158,7 +158,7 @@ impl SimpleAsyncComponent for EnvironmentApp { }; for (name, value) in &CONFIG.game.environment { - model.variables.guard().push_back((name.clone(), value.clone())); + model.variables.guard().push_back((name.trim().to_string(), value.trim().to_string())); } let variables = model.variables.widget(); @@ -175,8 +175,8 @@ impl SimpleAsyncComponent for EnvironmentApp { match msg { EnvironmentMsg::Add => { if let Ok(mut config) = config::get() { - let name = self.name.text().to_string(); - let value = self.value.text().to_string(); + let name = self.name.text().trim().to_string(); + let value = self.value.text().trim().to_string(); config.game.environment.insert(name.clone(), value.clone()); diff --git a/src/ui/preferences/general.rs b/src/ui/preferences/general.rs index 79b0cced..7d9bc8a1 100644 --- a/src/ui/preferences/general.rs +++ b/src/ui/preferences/general.rs @@ -9,14 +9,18 @@ use relm4::factory::{ use gtk::prelude::*; use adw::prelude::*; +use anime_launcher_sdk::anime_game_core::prelude::*; +use anime_launcher_sdk::wincompatlib::prelude::*; use anime_launcher_sdk::config; use anime_launcher_sdk::config::launcher::LauncherStyle; -use anime_launcher_sdk::anime_game_core::prelude::*; use anime_launcher_sdk::components::*; use anime_launcher_sdk::components::wine::WincompatlibWine; -use anime_launcher_sdk::wincompatlib::prelude::*; +use anime_launcher_sdk::env_emulation::Environment; +use anime_launcher_sdk::config::launcher::GameEdition; +use anime_launcher_sdk::anime_game_core::genshin::consts::GameEdition as CoreGameEdition; use super::main::PreferencesAppMsg; +use crate::ui::migrate_installation::MigrateInstallationApp; use crate::ui::components; use crate::ui::components::*; use crate::i18n::*; @@ -102,6 +106,7 @@ impl AsyncFactoryComponent for VoicePackageComponent { pub struct GeneralApp { voice_packages: AsyncFactoryVecDeque, + migrate_installation: Controller, wine_components: AsyncController>, dxvk_components: AsyncController>, @@ -145,7 +150,9 @@ pub enum GeneralAppMsg { RemoveVoicePackage(DynamicIndex), SetVoicePackageSensitivity(DynamicIndex, bool), + OpenMigrateInstallation, RepairGame, + WineOpen(&'static [&'static str]), UpdateLauncherStyle(LauncherStyle), @@ -272,8 +279,6 @@ impl SimpleAsyncComponent for GeneralApp { set_title: &tr("launcher-language"), set_subtitle: &tr("launcher-language-description"), - // TODO: maybe simplify it by some way? e.g. specify such stuff in i18n mod - set_model: Some(>k::StringList::new(&model.languages.iter().map(|lang| lang.as_str()).collect::>())), set_selected: { @@ -297,9 +302,77 @@ impl SimpleAsyncComponent for GeneralApp { } }, + adw::ComboRow { + set_title: &tr("game-edition"), + + set_model: Some(>k::StringList::new(&[ + &tr("global"), + &tr("china") + ])), + + set_selected: match CONFIG.launcher.edition { + GameEdition::Global => 0, + GameEdition::China => 1 + }, + + connect_selected_notify[sender] => move |row| { + if is_ready() { + #[allow(unused_must_use)] + if let Ok(mut config) = config::get() { + config.launcher.edition = match row.selected() { + 0 => GameEdition::Global, + 1 => GameEdition::China, + + _ => unreachable!() + }; + + // Select new game edition + CoreGameEdition::from(config.launcher.edition).select(); + + config::update(config); + + sender.output(PreferencesAppMsg::UpdateLauncherState); + } + } + } + }, + + adw::ComboRow { + set_title: &tr("game-environment"), + set_subtitle: &tr("game-environment-description"), + + set_model: Some(>k::StringList::new(&[ + "PC", + "Android" + ])), + + set_selected: match CONFIG.launcher.environment { + Environment::PC => 0, + Environment::Android => 1, + + _ => unreachable!() + }, + + connect_selected_notify => |row| { + if is_ready() { + if let Ok(mut config) = config::get() { + config.launcher.environment = match row.selected() { + 0 => Environment::PC, + 1 => Environment::Android, + + _ => unreachable!() + }; + + config::update(config); + } + } + } + }, + #[local_ref] voice_packages -> adw::ExpanderRow { - set_title: &tr("game-voiceovers") + set_title: &tr("game-voiceovers"), + set_subtitle: &tr("game-voiceovers-description") }, gtk::Box { @@ -307,6 +380,13 @@ impl SimpleAsyncComponent for GeneralApp { set_spacing: 8, set_margin_top: 16, + gtk::Button { + set_label: &tr("migrate-installation"), + set_tooltip_text: Some(&tr("migrate-installation-description")), + + connect_clicked => GeneralAppMsg::OpenMigrateInstallation + }, + gtk::Button { set_label: &tr("repair-game"), @@ -346,7 +426,7 @@ impl SimpleAsyncComponent for GeneralApp { VersionDiff::NotInstalled { .. } => &[] } - None => &["success"] + None => &[] }, #[watch] @@ -398,7 +478,12 @@ impl SimpleAsyncComponent for GeneralApp { PatchStatus::Preparation { .. } | PatchStatus::Testing { .. } => &["warning"], PatchStatus::Available { .. } => unsafe { - if let Ok(true) = model.unity_player_patch.as_ref().unwrap_unchecked().is_applied(&CONFIG.game.path) { + let path = match config::get() { + Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), + Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(), + }; + + if let Ok(true) = model.unity_player_patch.as_ref().unwrap_unchecked().is_applied(path) { &["success"] } else { &["warning"] @@ -420,7 +505,12 @@ impl SimpleAsyncComponent for GeneralApp { PatchStatus::Preparation { .. } => tr("patch-preparation-tooltip"), PatchStatus::Testing { .. } => tr("patch-testing-tooltip"), PatchStatus::Available { .. } => unsafe { - if let Ok(true) = model.unity_player_patch.as_ref().unwrap_unchecked().is_applied(&CONFIG.game.path) { + let path = match config::get() { + Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), + Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(), + }; + + if let Ok(true) = model.unity_player_patch.as_ref().unwrap_unchecked().is_applied(path) { String::new() } else { tr("patch-not-applied-tooltip") @@ -459,7 +549,12 @@ impl SimpleAsyncComponent for GeneralApp { PatchStatus::Preparation { .. } | PatchStatus::Testing { .. } => &["warning"], PatchStatus::Available { .. } => unsafe { - if let Ok(true) = model.xlua_patch.as_ref().unwrap_unchecked().is_applied(&CONFIG.game.path) { + let path = match config::get() { + Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), + Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(), + }; + + if let Ok(true) = model.xlua_patch.as_ref().unwrap_unchecked().is_applied(path) { &["success"] } else { &["warning"] @@ -481,7 +576,12 @@ impl SimpleAsyncComponent for GeneralApp { PatchStatus::Preparation { .. } => tr("patch-preparation-tooltip"), PatchStatus::Testing { .. } => tr("patch-testing-tooltip"), PatchStatus::Available { .. } => unsafe { - if let Ok(true) = model.xlua_patch.as_ref().unwrap_unchecked().is_applied(&CONFIG.game.path) { + let path = match config::get() { + Ok(config) => config.game.path.for_edition(config.launcher.edition).to_path_buf(), + Err(_) => CONFIG.game.path.for_edition(CONFIG.launcher.edition).to_path_buf(), + }; + + if let Ok(true) = model.xlua_patch.as_ref().unwrap_unchecked().is_applied(path) { String::new() } else { tr("patch-not-applied-tooltip") @@ -558,18 +658,18 @@ impl SimpleAsyncComponent for GeneralApp { #[watch] set_activatable: !model.selecting_wine_version, - #[watch] - set_icon_name: if model.selecting_wine_version { - Some("process-working-symbolic") - } else { - None - }, - connect_selected_notify[sender] => move |row| { if is_ready() { sender.input(GeneralAppMsg::SelectWine(row.selected() as usize)); } - } @wine_selected_notify + } @wine_selected_notify, + + add_suffix = >k::Spinner { + set_spinning: true, + + #[watch] + set_visible: model.selecting_wine_version + } }, adw::ActionRow { @@ -595,6 +695,57 @@ impl SimpleAsyncComponent for GeneralApp { add = model.wine_components.widget(), }, + add = &adw::PreferencesGroup { + adw::ExpanderRow { + set_title: &tr("wine-tools"), + + add_row = &adw::ActionRow { + set_title: &tr("command-line"), + set_subtitle: "start cmd", + + set_activatable: true, + + connect_activated => GeneralAppMsg::WineOpen(&["start", "cmd"]) + }, + + add_row = &adw::ActionRow { + set_title: &tr("registry-editor"), + set_subtitle: "regedit", + + set_activatable: true, + + connect_activated => GeneralAppMsg::WineOpen(&["regedit"]) + }, + + add_row = &adw::ActionRow { + set_title: &tr("explorer"), + set_subtitle: "explorer", + + set_activatable: true, + + connect_activated => GeneralAppMsg::WineOpen(&["explorer"]) + }, + + add_row = &adw::ActionRow { + set_title: &tr("task-manager"), + set_subtitle: "taskmgr", + + set_activatable: true, + + connect_activated => GeneralAppMsg::WineOpen(&["taskmgr"]) + }, + + add_row = &adw::ActionRow { + set_title: &tr("configuration"), + set_subtitle: "winecfg", + + set_activatable: true, + + connect_activated => GeneralAppMsg::WineOpen(&["winecfg"]) + } + } + }, + add = &adw::PreferencesGroup { set_title: &tr("dxvk-version"), @@ -622,18 +773,18 @@ impl SimpleAsyncComponent for GeneralApp { #[watch] set_activatable: !model.selecting_dxvk_version, - #[watch] - set_icon_name: if model.selecting_dxvk_version { - Some("process-working-symbolic") - } else { - None - }, - connect_selected_notify[sender] => move |row| { if is_ready() { sender.input(GeneralAppMsg::SelectDxvk(row.selected() as usize)); } - } @dxvk_selected_notify + } @dxvk_selected_notify, + + add_suffix = >k::Spinner { + set_spinning: true, + + #[watch] + set_visible: model.selecting_dxvk_version + } }, adw::ActionRow { @@ -674,6 +825,10 @@ impl SimpleAsyncComponent for GeneralApp { let mut model = Self { voice_packages: AsyncFactoryVecDeque::new(adw::ExpanderRow::new(), sender.input_sender()), + migrate_installation: MigrateInstallationApp::builder() + .launch(()) + .detach(), + wine_components: ComponentsList::builder() .launch(ComponentsListInit { pattern: ComponentsListPattern { @@ -684,9 +839,14 @@ impl SimpleAsyncComponent for GeneralApp { group.versions = group.versions.into_iter().take(12).collect(); let mut group: ComponentsListGroup = group.into(); + let mut recommended = 6; + + for i in 0..group.versions.len() { + if recommended > 0 && group.versions[i].recommended { + recommended -= 1; + } - if group.versions.len() > 6 { - for i in 6..group.versions.len() { + else { group.versions[i].recommended = false; } } @@ -710,9 +870,14 @@ impl SimpleAsyncComponent for GeneralApp { group.versions = group.versions.into_iter().take(12).collect(); let mut group: ComponentsListGroup = group.into(); + let mut recommended = 6; - if group.versions.len() > 6 { - for i in 6..group.versions.len() { + for i in 0..group.versions.len() { + if recommended > 0 && group.versions[i].recommended { + recommended -= 1; + } + + else { group.versions[i].recommended = false; } } @@ -809,10 +974,11 @@ impl SimpleAsyncComponent for GeneralApp { config::update(config.clone()); let package = VoicePackage::with_locale(package.locale).unwrap(); + let game_path = config.game.path.for_edition(config.launcher.edition).to_path_buf(); - if package.is_installed_in(&config.game.path) { + if package.is_installed_in(&game_path) { std::thread::spawn(move || { - if let Err(err) = package.delete_in(&config.game.path) { + if let Err(err) = package.delete_in(game_path) { tracing::error!("Failed to delete voice package: {:?}", package.locale()); sender.input(GeneralAppMsg::Toast { @@ -839,17 +1005,48 @@ impl SimpleAsyncComponent for GeneralApp { } } + GeneralAppMsg::OpenMigrateInstallation => unsafe { + if let Some(window) = crate::ui::main::PREFERENCES_WINDOW.as_ref() { + self.migrate_installation.widget().set_transient_for(Some(window.widget())); + } + + self.migrate_installation.widget().show(); + } + #[allow(unused_must_use)] GeneralAppMsg::RepairGame => { sender.output(Self::Output::RepairGame); } + GeneralAppMsg::WineOpen(executable) => { + let config = config::get().unwrap_or_else(|_| CONFIG.clone()); + + if let Ok(Some(wine)) = config.get_selected_wine() { + let result = wine.to_wine(config.components.path, Some(config.game.wine.builds.join(&wine.name))) + .with_prefix(config.game.wine.prefix) + .with_loader(WineLoader::Current) + .with_arch(WineArch::Win64) + .run_args(executable); + + if let Err(err) = result { + sender.input(GeneralAppMsg::Toast { + title: tr_args("wine-run-error", [ + ("executable", executable.join(" ").into()) + ]), + description: Some(err.to_string()) + }); + + tracing::error!("Failed to run {:?} using wine: {err}", executable); + } + } + } + #[allow(unused_must_use)] GeneralAppMsg::UpdateLauncherStyle(style) => { if style == LauncherStyle::Classic && !KEEP_BACKGROUND_FILE.exists() { if let Err(err) = crate::background::download_background() { tracing::error!("Failed to download background picture"); - + sender.input(GeneralAppMsg::Toast { title: tr("background-downloading-failed"), description: Some(err.to_string()) @@ -884,16 +1081,13 @@ impl SimpleAsyncComponent for GeneralApp { self.downloaded_wine_versions = wine::get_downloaded(&CONFIG.components.path, &CONFIG.game.wine.builds) .unwrap_or_default() .into_iter() - .flat_map(|group| group.versions - .into_iter() - .map(move |version| ( - version.clone(), - version.features.unwrap_or_else( - || group.features.to_owned().unwrap_or_default() - )) - ) - ) - .collect(); + .flat_map(|group| group.versions.clone().into_iter() + .map(move |version| { + let features = version.features_in(&group).unwrap_or_default(); + + (version, features) + }) + ).collect(); self.selected_wine_version = if let Some(selected) = &CONFIG.game.wine.selected { let mut index = 0; diff --git a/src/ui/preferences/main.rs b/src/ui/preferences/main.rs index 1d232a38..a95c80c6 100644 --- a/src/ui/preferences/main.rs +++ b/src/ui/preferences/main.rs @@ -57,11 +57,10 @@ impl SimpleAsyncComponent for PreferencesApp { preferences_window = adw::PreferencesWindow { set_title: Some(&tr("preferences")), set_default_size: (700, 560), + set_hide_on_close: true, set_modal: true, - - // FIXME: doesn't work for any reason - set_search_enabled: false, + set_search_enabled: true, add = model.general.widget(), add = model.enhancements.widget(),