mirror of
https://github.com/an-anime-team/sleepy-launcher.git
synced 2025-03-15 14:38:29 +03:00
feat: added game repairing function
This commit is contained in:
parent
e008a97822
commit
3c7eba4d79
10 changed files with 209 additions and 11 deletions
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added game repairing function
|
||||
|
||||
### Fixed
|
||||
|
||||
- Forced `format_lang` to return regions for language codes
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed default language from en to en-us
|
||||
|
||||
## 3.0.1
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -8,6 +8,9 @@ failed-get-selected-wine = Die ausgewählte Wine version konnte nicht abgerufen
|
|||
downloading-failed = Herunterladen fehlgeschlagen
|
||||
unpacking-failed = Entpacken fehlgeschlagen
|
||||
|
||||
game-file-repairing-error = Failed to repair game file
|
||||
integrity-files-getting-error = Failed to get integrity files
|
||||
|
||||
background-downloading-failed = Download des Hintergrundbildes fehlgeschlagen
|
||||
config-update-error = Speichern der Konfiguration fehlgeschlagen
|
||||
wine-prefix-update-failed = Aktualisierung des wine prefix fehlgeschlagen
|
||||
|
|
|
@ -35,6 +35,8 @@ loading-launcher-state--patch = Launcher status am laden: Verifizierung des inst
|
|||
checking-free-space = Überprüfe Freien Speicherplatz
|
||||
downloading = Lade Herunter
|
||||
unpacking = Entpacken
|
||||
verifying-files = Verifying files
|
||||
repairing-files = Repairing files
|
||||
|
||||
|
||||
launch = Starten
|
||||
|
|
|
@ -8,6 +8,9 @@ failed-get-selected-wine = Failed to get selected wine version
|
|||
downloading-failed = Downloading failed
|
||||
unpacking-failed = Unpacking failed
|
||||
|
||||
game-file-repairing-error = Failed to repair game file
|
||||
integrity-files-getting-error = Failed to get integrity files
|
||||
|
||||
background-downloading-failed = Failed to download background picture
|
||||
config-update-error = Failed to save config
|
||||
wine-prefix-update-failed = Failed to update wine prefix
|
||||
|
|
|
@ -35,6 +35,8 @@ loading-launcher-state--patch = Loading launcher state: verifying installed patc
|
|||
checking-free-space = Checking free space
|
||||
downloading = Downloading
|
||||
unpacking = Unpacking
|
||||
verifying-files = Verifying files
|
||||
repairing-files = Repairing files
|
||||
|
||||
|
||||
launch = Launch
|
||||
|
|
|
@ -8,6 +8,9 @@ failed-get-selected-wine = Не удалось найти выбранную в
|
|||
downloading-failed = Ошибка загрузки
|
||||
unpacking-failed = Ошибка распаковки
|
||||
|
||||
game-file-repairing-error = Не удалось починить игровой файл
|
||||
integrity-files-getting-error = Не удалось получить верные данные о файлах игры
|
||||
|
||||
background-downloading-failed = Не удалось загрузить фоновое изображение
|
||||
config-update-error = Ошибка сохранения настроек
|
||||
wine-prefix-update-failed = Ошибка обновления префикса Wine
|
||||
|
|
|
@ -42,6 +42,8 @@ loading-launcher-state--patch = Загрузка статуса лаунчера
|
|||
checking-free-space = Проверка свободного места
|
||||
downloading = Загрузка
|
||||
unpacking = Распаковка
|
||||
verifying-files = Проверка файлов
|
||||
repairing-files = Починка файлов
|
||||
|
||||
|
||||
launch = Запустить
|
||||
|
|
167
src/ui/main.rs
167
src/ui/main.rs
|
@ -15,6 +15,8 @@ use anime_launcher_sdk::states::LauncherState;
|
|||
use anime_launcher_sdk::wincompatlib::prelude::*;
|
||||
use anime_launcher_sdk::components::wine;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use crate::*;
|
||||
use crate::i18n::*;
|
||||
use crate::ui::components::*;
|
||||
|
@ -73,11 +75,13 @@ pub enum AppMsg {
|
|||
SetLauncherStyle(LauncherStyle),
|
||||
SetLoadingStatus(Option<Option<String>>),
|
||||
|
||||
OpenPreferences,
|
||||
ClosePreferences,
|
||||
SetDownloading(bool),
|
||||
DisableButtons(bool),
|
||||
|
||||
OpenPreferences,
|
||||
ClosePreferences,
|
||||
RepairGame,
|
||||
|
||||
PredownloadUpdate,
|
||||
PerformAction,
|
||||
|
||||
|
@ -739,6 +743,14 @@ impl SimpleComponent for App {
|
|||
self.style = style;
|
||||
}
|
||||
|
||||
AppMsg::SetDownloading(state) => {
|
||||
self.downloading = state;
|
||||
}
|
||||
|
||||
AppMsg::DisableButtons(state) => {
|
||||
self.disabled_buttons = state;
|
||||
}
|
||||
|
||||
AppMsg::OpenPreferences => unsafe {
|
||||
PREFERENCES_WINDOW.as_ref().unwrap_unchecked().widget().show();
|
||||
}
|
||||
|
@ -747,12 +759,151 @@ impl SimpleComponent for App {
|
|||
PREFERENCES_WINDOW.as_ref().unwrap_unchecked().widget().hide();
|
||||
}
|
||||
|
||||
AppMsg::SetDownloading(state) => {
|
||||
self.downloading = state;
|
||||
}
|
||||
#[allow(unused_must_use)]
|
||||
AppMsg::RepairGame => {
|
||||
let config = config::get().unwrap();
|
||||
|
||||
AppMsg::DisableButtons(state) => {
|
||||
self.disabled_buttons = state;
|
||||
let progress_bar_input = self.progress_bar.sender().clone();
|
||||
|
||||
progress_bar_input.send(ProgressBarMsg::UpdateCaption(Some(tr("verifying-files"))));
|
||||
|
||||
self.downloading = true;
|
||||
|
||||
std::thread::spawn(move || {
|
||||
match repairer::try_get_integrity_files(None) {
|
||||
Ok(mut files) => {
|
||||
// Add voiceovers files
|
||||
let game = Game::new(&config.game.path);
|
||||
|
||||
if let Ok(voiceovers) = game.get_voice_packages() {
|
||||
for package in voiceovers {
|
||||
if let Ok(mut voiceover_files) = repairer::try_get_voice_integrity_files(package.locale(), None) {
|
||||
files.append(&mut voiceover_files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
progress_bar_input.send(ProgressBarMsg::UpdateProgress(0, 0));
|
||||
|
||||
let mut total = 0;
|
||||
|
||||
for file in &files {
|
||||
total += file.size;
|
||||
}
|
||||
|
||||
let median_size = total / config.launcher.repairer.threads;
|
||||
let mut i = 0;
|
||||
|
||||
let (verify_sender, verify_receiver) = std::sync::mpsc::channel();
|
||||
|
||||
for _ in 0..config.launcher.repairer.threads {
|
||||
let mut thread_files = Vec::new();
|
||||
let mut thread_files_size = 0;
|
||||
|
||||
while i < files.len() {
|
||||
thread_files.push(files[i].clone());
|
||||
|
||||
thread_files_size += files[i].size;
|
||||
i += 1;
|
||||
|
||||
if thread_files_size >= median_size {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let game_path = config.game.path.clone();
|
||||
let thread_sender = verify_sender.clone();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
for file in thread_files {
|
||||
let status = if config.launcher.repairer.fast {
|
||||
file.fast_verify(&game_path)
|
||||
} else {
|
||||
file.verify(&game_path)
|
||||
};
|
||||
|
||||
thread_sender.send((file, status)).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// We have [config.launcher.repairer.threads] copies of this sender + the original one
|
||||
// receiver will return Err when all the senders will be dropped.
|
||||
// [config.launcher.repairer.threads] senders will be dropped when threads will finish verifying files
|
||||
// but this one will live as long as current thread exists so we should drop it manually
|
||||
drop(verify_sender);
|
||||
|
||||
let mut broken = Vec::new();
|
||||
let mut processed = 0;
|
||||
|
||||
while let Ok((file, status)) = verify_receiver.recv() {
|
||||
processed += file.size;
|
||||
|
||||
if !status {
|
||||
broken.push(file);
|
||||
}
|
||||
|
||||
progress_bar_input.send(ProgressBarMsg::UpdateProgress(processed, total));
|
||||
}
|
||||
|
||||
if !broken.is_empty() {
|
||||
progress_bar_input.send(ProgressBarMsg::UpdateCaption(Some(tr("repairing-files"))));
|
||||
progress_bar_input.send(ProgressBarMsg::UpdateProgress(0, 0));
|
||||
|
||||
tracing::warn!("Found broken files:\n{}", broken.iter().fold(String::new(), |acc, file| acc + &format!("- {}\n", file.path.to_string_lossy())));
|
||||
|
||||
let total = broken.len() as f64;
|
||||
|
||||
let is_patch_applied = match Patch::try_fetch(config.patch.servers, anime_launcher_sdk::consts::PATCH_FETCHING_TIMEOUT) {
|
||||
Ok(patch) => patch.is_applied(&config.game.path).unwrap_or(true),
|
||||
Err(_) => true
|
||||
};
|
||||
|
||||
tracing::debug!("Patch status: {}", is_patch_applied);
|
||||
|
||||
fn should_ignore(path: &Path) -> bool {
|
||||
for part in ["UnityPlayer.dll", "xlua.dll", "crashreport.exe", "upload_crash.exe", "vulkan-1.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 let Err(err) = file.repair(&config.game.path) {
|
||||
let err: std::io::Error = err.into();
|
||||
|
||||
sender.input(AppMsg::Toast {
|
||||
title: tr("game-file-repairing-error"),
|
||||
description: Some(err.to_string())
|
||||
});
|
||||
|
||||
tracing::error!("Failed to repair game file: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
progress_bar_input.send(ProgressBarMsg::UpdateProgress(i as u64, total as u64));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to get inregrity failes: {err}");
|
||||
|
||||
sender.input(AppMsg::Toast {
|
||||
title: tr("integrity-files-getting-error"),
|
||||
description: Some(err.to_string())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
sender.input(AppMsg::SetDownloading(false));
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(unused_must_use)]
|
||||
|
@ -784,6 +935,8 @@ impl SimpleComponent for App {
|
|||
description: Some(err.to_string())
|
||||
});
|
||||
|
||||
tracing::error!("Failed to predownload update: {err}");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,6 +138,8 @@ pub enum GeneralAppMsg {
|
|||
RemoveVoicePackage(DynamicIndex),
|
||||
SetVoicePackageSensitivity(DynamicIndex, bool),
|
||||
|
||||
RepairGame,
|
||||
|
||||
UpdateLauncherStyle(LauncherStyle),
|
||||
|
||||
WineRecommendedOnly(bool),
|
||||
|
@ -293,16 +295,17 @@ impl SimpleAsyncComponent for GeneralApp {
|
|||
set_title: &tr("game-voiceovers")
|
||||
},
|
||||
|
||||
// TODO for 3.1.0
|
||||
/*gtk::Box {
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
set_spacing: 8,
|
||||
set_margin_top: 16,
|
||||
|
||||
gtk::Button {
|
||||
set_label: &tr("repair-game")
|
||||
set_label: &tr("repair-game"),
|
||||
|
||||
connect_clicked => GeneralAppMsg::RepairGame
|
||||
}
|
||||
}*/
|
||||
}
|
||||
},
|
||||
|
||||
add = &adw::PreferencesGroup {
|
||||
|
@ -656,6 +659,11 @@ impl SimpleAsyncComponent for GeneralApp {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(unused_must_use)]
|
||||
GeneralAppMsg::RepairGame => {
|
||||
sender.output(Self::Output::RepairGame);
|
||||
}
|
||||
|
||||
#[allow(unused_must_use)]
|
||||
GeneralAppMsg::UpdateLauncherStyle(style) => {
|
||||
if style == LauncherStyle::Classic && !KEEP_BACKGROUND_FILE.exists() {
|
||||
|
|
|
@ -35,6 +35,7 @@ pub enum PreferencesAppMsg {
|
|||
SetLauncherStyle(LauncherStyle),
|
||||
|
||||
UpdateLauncherState,
|
||||
RepairGame,
|
||||
|
||||
Toast {
|
||||
title: String,
|
||||
|
@ -141,6 +142,13 @@ impl SimpleAsyncComponent for PreferencesApp {
|
|||
});
|
||||
}
|
||||
|
||||
#[allow(unused_must_use)]
|
||||
PreferencesAppMsg::RepairGame => unsafe {
|
||||
PREFERENCES_WINDOW.as_ref().unwrap_unchecked().close();
|
||||
|
||||
sender.output(Self::Output::RepairGame);
|
||||
}
|
||||
|
||||
PreferencesAppMsg::Toast { title, description } => unsafe {
|
||||
let toast = adw::Toast::new(&title);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue