Merge pull request #123 from an-anime-team/next

Release 3.4.0
This commit is contained in:
Observer KRypt0n_ 2023-04-11 22:46:37 +02:00 committed by GitHub
commit b7718d411c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 1093 additions and 355 deletions

3
.gitmodules vendored
View file

@ -1,3 +0,0 @@
[submodule "anime-launcher-sdk"]
path = anime-launcher-sdk
url = https://github.com/an-anime-team/anime-launcher-sdk

View file

@ -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

253
Cargo.lock generated
View file

@ -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"
@ -224,10 +237,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "base64"
version = "0.13.1"
name = "backtrace"
version = "0.3.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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",

View file

@ -1,6 +1,6 @@
[package]
name = "anime-game-launcher"
version = "3.3.0"
version = "3.4.0"
description = "Anime Game launcher"
authors = ["Nikita Podvirnyy <suimin.tu.mu.ga.mi@gmail.com>"]
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"

View file

@ -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 |

@ -1 +0,0 @@
Subproject commit 19ceddca82367514b5c806cfbc94f527ccd75167

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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é

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 = Ошибка загрузки

View file

@ -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 = Выбрать языковые пакеты

View file

@ -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

View file

@ -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 = Создать префикс

View file

@ -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

View file

@ -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ç

View file

@ -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

View file

@ -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

View file

@ -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 版本失败

View file

@ -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 = 选择语音包

View file

@ -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 版本

View file

@ -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

View file

@ -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<Background> {
let json = serde_json::from_slice::<serde_json::Value>(&fetch(get_uri(), None)?.get_body()?)?;
let json = serde_json::from_slice::<serde_json::Value>(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);
}

View file

@ -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");

26
src/move_folder.rs Normal file
View file

@ -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(())
}

View file

@ -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: &[
"<p>Added</p>",
"<ul>",
"<li>Added installation migration feature</li>",
"<li>Added game environment switcher</li>",
"<li>Added game edition switcher</li>",
"<li>Added changelog to updated components toast</li>",
"<li>Added wine tools to settings</li>",
"<li>Added preferences search</li>",
"<li>Added new progress bar statuses for applyign hdiff patches and removing outdated files</li>",
"<li>Added automatic 3.5 -> 3.6 voiceover files migration related to changed files structure</li>",
"</ul>",
"<p>Fixed</p>",
"<ul>",
"<li>Added whitespaces removing from environment values</li>",
"</ul>",
"<p>Changed</p>",
"<ul>",
"<li>Improved game repairing feature</li>",
"<li>Replaced curl dependency by native code</li>",
"<li>Replaced static image by spinner in wine / dxvk version selection</li>",
"<li>Made wine / dxvk versions always visible if they're downloaded</li>",
"</ul>",
].join("\n"),
set_modal: true,
set_hide_on_close: true,

View file

@ -25,6 +25,7 @@ pub struct ComponentsListGroup {
}
impl From<wine::Group> for ComponentsListGroup {
#[inline]
fn from(group: wine::Group) -> Self {
Self {
title: group.title,
@ -34,6 +35,7 @@ impl From<wine::Group> for ComponentsListGroup {
}
impl From<dxvk::Group> for ComponentsListGroup {
#[inline]
fn from(group: dxvk::Group) -> Self {
Self {
title: group.title,
@ -51,23 +53,33 @@ pub struct ComponentsListVersion {
}
impl From<wine::Version> 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<dxvk::Version> 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
}
}
}

View file

@ -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"),
InstallerUpdate::DownloadingError(err) => tracing::error!("Downloading error: {:?}", err),
InstallerUpdate::UnpackingError(err) => tracing::error!("Unpacking error: {:?}", err)
DiffUpdate::ApplyingHdiffFinished => tracing::info!("Applying hdiffs finished"),
DiffUpdate::RemovingOutdatedFinished => tracing::info!("Removing outdated files finished"),
DiffUpdate::InstallerUpdate(InstallerUpdate::DownloadingError(err)) => tracing::error!("Downloading error: {:?}", err),
DiffUpdate::InstallerUpdate(InstallerUpdate::UnpackingError(err)) => tracing::error!("Unpacking error: {:?}", err)
}
}

View file

@ -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 = &gtk::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)));
});
}));
}

View file

@ -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<ProgressBar>,
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.to_str().unwrap(),
set_subtitle: model.game_global.to_str().unwrap(),
connect_activated => DefaultPathsAppMsg::ChoosePath(Folders::Game)
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_china.to_str().unwrap(),
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<Self>,
) -> AsyncComponentParts<Self> {
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());

View file

@ -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)));
});
}

View file

@ -17,7 +17,7 @@ use super::select_voiceovers::*;
use super::download_components::*;
use super::finish::*;
pub static mut MAIN_WINDOW: Option<adw::Window> = None;
pub static mut MAIN_WINDOW: Option<adw::ApplicationWindow> = 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");

View file

@ -21,7 +21,7 @@ pub fn apply_patch<T: PatchExt + Send + Sync + 'static>(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 {

View file

@ -18,11 +18,11 @@ pub fn download_diff(sender: ComponentSender<App>, progress_bar_input: Sender<Pr
std::thread::spawn(move || {
let config = config::get().unwrap();
let game_path = config.game.path.for_edition(config.launcher.edition).to_path_buf();
#[allow(unused_must_use)]
let result = diff.install_to_by(config.game.path, config.launcher.temp, clone!(@strong 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<App>, progress_bar_input: Sender<Pr
});
}
InstallerUpdate::UnpackingError(err) => {
DiffUpdate::InstallerUpdate(InstallerUpdate::UnpackingError(err)) => {
tracing::error!("Unpacking failed: {err}");
sender.input(AppMsg::Toast {
@ -43,7 +43,9 @@ pub fn download_diff(sender: ComponentSender<App>, progress_bar_input: Sender<Pr
_ => ()
}
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;

View file

@ -55,7 +55,6 @@ pub fn download_wine(sender: ComponentSender<App>, progress_bar_input: Sender<Pr
sender.input(AppMsg::SetDownloading(true));
std::thread::spawn(clone!(@strong 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<App>, progress_bar_input: Sender<Pr
_ => ()
}
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());

View file

@ -0,0 +1,25 @@
use relm4::prelude::*;
use std::path::PathBuf;
use crate::*;
use super::{App, AppMsg};
pub fn migrate_folder(sender: ComponentSender<App>, from: PathBuf, to: PathBuf, cleanup_folder: Option<PathBuf>) {
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
});
});
}

View file

@ -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<adw::ApplicationWindow> = None;
static mut PREFERENCES_WINDOW: Option<AsyncController<PreferencesApp>> = None;
static mut ABOUT_DIALOG: Option<Controller<AboutDialog>> = None;
pub static mut MAIN_WINDOW: Option<adw::ApplicationWindow> = None;
pub static mut PREFERENCES_WINDOW: Option<AsyncController<PreferencesApp>> = None;
pub static mut ABOUT_DIALOG: Option<Controller<AboutDialog>> = None;
pub struct App {
progress_bar: AsyncController<ProgressBar>,
@ -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::<GameFolder>(&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::<ConfigFile>(&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::<WishUrl>(&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::<Vec<_>>()
.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(_) => ()

View file

@ -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<App>, progress_bar_input: Sender<Prog
match repairer::try_get_integrity_files(None) {
Ok(mut files) => {
// 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<App>, progress_bar_input: Sender<Prog
}
}
let game_path = config.game.path.clone();
let thread_sender = verify_sender.clone();
std::thread::spawn(move || {
std::thread::spawn(clone!(@strong game_path => 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<App>, progress_bar_input: Sender<Prog
thread_sender.send((file, status)).unwrap();
}
});
}));
}
// We have [config.launcher.repairer.threads] copies of this sender + the original one
@ -104,27 +106,48 @@ pub fn repair_game(sender: ComponentSender<App>, progress_bar_input: Sender<Prog
let total = broken.len() as f64;
// TODO: properly handle xlua patch
let is_patch_applied = UnityPlayerPatch::from_folder(&config.patch.path).unwrap()
.is_applied(&config.game.path).unwrap();
let player_patch = UnityPlayerPatch::from_folder(&config.patch.path).unwrap()
.is_applied(&game_path).unwrap();
tracing::debug!("Patch status: {}", is_patch_applied);
let xlua_patch = XluaPatch::from_folder(&config.patch.path).unwrap()
.is_applied(&game_path).unwrap();
fn should_ignore(path: &Path) -> bool {
for part in ["UnityPlayer.dll", "xlua.dll", "crashreport.exe", "upload_crash.exe", "vulkan-1.dll"] {
tracing::debug!("Patches status: player({player_patch}), xlua({xlua_patch})");
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<App>, progress_bar_input: Sender<Prog
}
}
else {
tracing::debug!("Skipped file: {}", file.path.to_string_lossy());
}
progress_bar_input.send(ProgressBarMsg::UpdateProgress(i as u64, total as u64));
}
}

View file

@ -0,0 +1,57 @@
use relm4::prelude::*;
use relm4::component::*;
use gtk::prelude::*;
use crate::i18n::*;
use super::first_run::default_paths::DefaultPathsApp;
pub struct MigrateInstallationApp {
default_paths: AsyncController<DefaultPathsApp>,
}
#[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<Self>,
) -> ComponentParts<Self> {
tracing::info!("Initializing migration window");
let model = Self {
default_paths: DefaultPathsApp::builder()
.launch(true)
.detach()
};
let widgets = view_output!();
ComponentParts { model, widgets }
}
}

View file

@ -3,3 +3,4 @@ pub mod about;
pub mod preferences;
pub mod components;
pub mod first_run;
pub mod migrate_installation;

View file

@ -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());

View file

@ -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<VoicePackageComponent>,
migrate_installation: Controller<MigrateInstallationApp>,
wine_components: AsyncController<ComponentsList<GeneralAppMsg>>,
dxvk_components: AsyncController<ComponentsList<GeneralAppMsg>>,
@ -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(&gtk::StringList::new(&model.languages.iter().map(|lang| lang.as_str()).collect::<Vec<&str>>())),
set_selected: {
@ -297,9 +302,77 @@ impl SimpleAsyncComponent for GeneralApp {
}
},
adw::ComboRow {
set_title: &tr("game-edition"),
set_model: Some(&gtk::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(&gtk::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 = &gtk::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 = &gtk::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;
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;
}
}
@ -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;

View file

@ -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(),