0.4.0: Add game switching, implement proper error handling thru out installer, and splash screen, probably more.
This commit is contained in:
parent
898f32f38c
commit
5239d2cd98
6 changed files with 238 additions and 97 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -3155,7 +3155,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reversed-rooms-launcher"
|
name = "reversed-rooms-launcher"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"directories",
|
"directories",
|
||||||
"file-format",
|
"file-format",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
[package]
|
[package]
|
||||||
name = "reversed-rooms-launcher"
|
name = "reversed-rooms-launcher"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
@ -469,8 +469,8 @@ impl Video {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the size/resolution of the video as `(width, height)`.
|
/// Get the size/resolution of the video as `(width, height)`.
|
||||||
pub fn size(&self) -> (i32, i32) {
|
pub fn size(&self) -> (u32, u32) {
|
||||||
(self.read().width, self.read().height)
|
(self.read().width.try_into().unwrap(), self.read().height.try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the framerate of the video as frames per second.
|
/// Get the framerate of the video as frames per second.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::{fs::create_dir_all, io::Write, path::Path};
|
use std::{fs::create_dir_all, io::Write, path::Path};
|
||||||
use std::fs::OpenOptions;
|
use std::fs::{DirEntry, OpenOptions};
|
||||||
|
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use reqwest::blocking::Client;
|
use reqwest::blocking::Client;
|
||||||
|
@ -150,42 +151,61 @@ fn refresh_kuro_install(proj_dirs: &ProjectDirs, client: &Client) -> Result<(),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_file_if_needed(dir: &Path, client: &Client, file_url: &str, file_type: &str) -> Result<(), String> {
|
fn update_file_if_needed(dir: &Path, client: &Client, file_url: &str, file_type: &str) -> Result<(), String> {
|
||||||
|
let current_file: Option<DirEntry> = {
|
||||||
|
let mut current_file = None;
|
||||||
|
for path in dir.read_dir().unwrap() {
|
||||||
|
let path = path.unwrap();
|
||||||
|
if path.file_name().into_string().unwrap().starts_with(format!("{}_", file_type).as_str()) {
|
||||||
|
current_file = Some(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current_file
|
||||||
|
};
|
||||||
|
|
||||||
let filename = extract_filename_from_url(file_url);
|
let filename = extract_filename_from_url(file_url);
|
||||||
let expected_file = format!("{}_{}", file_type, filename);
|
let expected_file = format!("{}_{}", file_type, filename);
|
||||||
let file_path = dir.join(&expected_file);
|
let file_path = dir.join(&expected_file);
|
||||||
|
|
||||||
let file_exists = file_path.exists();
|
if let Some(file) = current_file {
|
||||||
|
if filename != file.file_name().into_string().unwrap().strip_prefix(format!("{}_", file_type).as_str()).unwrap() {
|
||||||
if !file_exists {
|
update_file(client, file_path, file_type, file_url)?;
|
||||||
eprintln!("Downloading {} file from: {}", file_type, file_url);
|
} else {
|
||||||
let file_bytes = client
|
eprintln!("{} file already exists at: {}", file_type, file.path().display());
|
||||||
.get(file_url)
|
}
|
||||||
.send()
|
|
||||||
.map_err(|e| format!("Failed to send request for {} file: {}", file_type, e))?
|
|
||||||
.bytes()
|
|
||||||
.map_err(|e| format!("Failed to get bytes for {} file: {}", file_type, e))?;
|
|
||||||
|
|
||||||
let mut file = OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.truncate(true)
|
|
||||||
.open(&file_path)
|
|
||||||
.map_err(|e| format!("Failed to create {} file ({}): {}", file_type, file_path.display(), e))?;
|
|
||||||
|
|
||||||
file.write_all(&file_bytes)
|
|
||||||
.map_err(|e| format!("Failed to write {} file: {}", file_type, e))?;
|
|
||||||
|
|
||||||
file.flush()
|
|
||||||
.map_err(|e| format!("Failed to flush {} file: {}", file_type, e))?;
|
|
||||||
|
|
||||||
eprintln!("Successfully downloaded {} file to: {}", file_type, file_path.display());
|
|
||||||
} else {
|
} else {
|
||||||
eprintln!("{} file already exists at: {}", file_type, file_path.display());
|
update_file(client, file_path, file_type, file_url)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_file(client: &Client, file_path: PathBuf, file_type: &str, file_url: &str) -> Result<(), String> {
|
||||||
|
eprintln!("Downloading {} file from: {}", file_type, file_url);
|
||||||
|
let file_bytes = client
|
||||||
|
.get(file_url)
|
||||||
|
.send()
|
||||||
|
.map_err(|e| format!("Failed to send request for {} file: {}", file_type, e))?
|
||||||
|
.bytes()
|
||||||
|
.map_err(|e| format!("Failed to get bytes for {} file: {}", file_type, e))?;
|
||||||
|
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.truncate(true)
|
||||||
|
.open(&file_path)
|
||||||
|
.map_err(|e| format!("Failed to create {} file ({}): {}", file_type, file_path.display(), e))?;
|
||||||
|
|
||||||
|
file.write_all(&file_bytes)
|
||||||
|
.map_err(|e| format!("Failed to write {} file: {}", file_type, e))?;
|
||||||
|
|
||||||
|
file.flush()
|
||||||
|
.map_err(|e| format!("Failed to flush {} file: {}", file_type, e))?;
|
||||||
|
|
||||||
|
eprintln!("Successfully downloaded {} file to: {}", file_type, file_path.display());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn refresh_hoyo_install(proj_dirs: &ProjectDirs, client: &Client) -> Result<(), String> {
|
fn refresh_hoyo_install(proj_dirs: &ProjectDirs, client: &Client) -> Result<(), String> {
|
||||||
let hoyo_url = "https://sg-hyp-api.hoyoverse.com/hyp/hyp-connect/api/getGames?launcher_id=VYTpXlbWo8&language=en-us";
|
let hoyo_url = "https://sg-hyp-api.hoyoverse.com/hyp/hyp-connect/api/getGames?launcher_id=VYTpXlbWo8&language=en-us";
|
||||||
|
|
||||||
|
@ -227,6 +247,9 @@ fn refresh_hoyo_install(proj_dirs: &ProjectDirs, client: &Client) -> Result<(),
|
||||||
|
|
||||||
let icon_url = &game.display.icon.url;
|
let icon_url = &game.display.icon.url;
|
||||||
update_file_if_needed(data_dir, client, icon_url, "icon")?;
|
update_file_if_needed(data_dir, client, icon_url, "icon")?;
|
||||||
|
|
||||||
|
let bg_url = &game.display.background.url;
|
||||||
|
update_file_if_needed(data_dir, client, bg_url, "background")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
201
src/main.rs
201
src/main.rs
|
@ -1,5 +1,4 @@
|
||||||
// TODO: fix loading the save data lol
|
// #![windows_subsystem = "windows"]
|
||||||
#![windows_subsystem = "windows"]
|
|
||||||
mod utils;
|
mod utils;
|
||||||
mod components;
|
mod components;
|
||||||
|
|
||||||
|
@ -7,13 +6,13 @@ use components::installer::refresh_install;
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use ::image::{DynamicImage, ImageReader};
|
use ::image::{DynamicImage, ImageReader};
|
||||||
use iced::{
|
use iced::{
|
||||||
alignment::Vertical::{Bottom, Top}, border, event, font::{Stretch, Weight}, mouse::Interaction, widget::{button, column, container, image, mouse_area, opaque, row, stack, text, Space}, window::{self, icon, Settings}, Alignment::Center, Color, Element, Event, Font, Length, Padding, Size, Subscription, Task
|
alignment::Vertical::{Bottom, Top}, border, event, font::{Stretch, Weight}, mouse::Interaction, widget::{button, center, column, container, image, mouse_area, opaque, row, stack, text, Space}, window::{self, icon, Settings}, Alignment::Center, Color, Element, Event, Font, Length, Padding, Size, Subscription, Task
|
||||||
};
|
};
|
||||||
use iced_video_player::{Video, VideoPlayer};
|
use iced_video_player::{Video, VideoPlayer};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum::IntoEnumIterator;
|
use strum::IntoEnumIterator;
|
||||||
use strum_macros::EnumIter;
|
use strum_macros::EnumIter;
|
||||||
use utils::visual_helper::{get_game_background, get_game_icon_dynamic_image, style_container};
|
use utils::visual_helper::{get_game_background, get_game_icon_dynamic_image, get_game_splash_dynamic_image, style_container};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap, env, fs::{self, create_dir_all, read_to_string}, io::{Cursor, Write}, path::PathBuf
|
collections::HashMap, env, fs::{self, create_dir_all, read_to_string}, io::{Cursor, Write}, path::PathBuf
|
||||||
};
|
};
|
||||||
|
@ -77,6 +76,21 @@ enum PossibleGames {
|
||||||
GenshinImpact,
|
GenshinImpact,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait IdentifibleGameType {
|
||||||
|
fn get_game_preferred_size(&self) -> (u32, u32);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdentifibleGameType for PossibleGames {
|
||||||
|
fn get_game_preferred_size(&self) -> (u32, u32) {
|
||||||
|
match self {
|
||||||
|
PossibleGames::WutheringWaves => (1280, 760),
|
||||||
|
PossibleGames::HonkaiStarRail => (1280, 720),
|
||||||
|
PossibleGames::ZenlessZoneZero => (1280, 720),
|
||||||
|
PossibleGames::GenshinImpact => (1280, 720),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum Launcher {
|
enum Launcher {
|
||||||
Loaded(Box<State>),
|
Loaded(Box<State>),
|
||||||
|
@ -95,17 +109,21 @@ struct State {
|
||||||
installed_game_servers: Vec<PossibleGames>,
|
installed_game_servers: Vec<PossibleGames>,
|
||||||
db_software_installed: bool,
|
db_software_installed: bool,
|
||||||
background: Option<LauncherBackground>,
|
background: Option<LauncherBackground>,
|
||||||
|
splash_images: HashMap<PossibleGames, DynamicImage>,
|
||||||
icon_images: HashMap<PossibleGames, DynamicImage>
|
icon_images: HashMap<PossibleGames, DynamicImage>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
fn get_background_element(&self) -> Element<Message> {
|
fn get_background_element(&self) -> Option<Element<Message>> {
|
||||||
match self.background.as_ref().unwrap() {
|
if let Some(background) = self.background.as_ref() {
|
||||||
LauncherBackground::Video(video) => VideoPlayer::new(video).into(),
|
match background {
|
||||||
LauncherBackground::Image(img) => image(image::Handle::from_rgba(img.width(), img.height(), img.to_rgba8().into_raw())).into(),
|
LauncherBackground::Video(video) => Some(VideoPlayer::new(video).into()),
|
||||||
|
LauncherBackground::Image(img) => Some(image(image::Handle::from_rgba(img.width(), img.height(), img.to_rgba8().into_raw())).into()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_game_icon_row(&self) -> Element<Message> {
|
fn get_game_icon_row(&self) -> Element<Message> {
|
||||||
container(row![
|
container(row![
|
||||||
self.create_game_icon(&PossibleGames::WutheringWaves),
|
self.create_game_icon(&PossibleGames::WutheringWaves),
|
||||||
|
@ -117,23 +135,36 @@ impl State {
|
||||||
).align_x(Center).width(Length::Fill).into()
|
).align_x(Center).width(Length::Fill).into()
|
||||||
}
|
}
|
||||||
fn create_game_icon(&self, game: &PossibleGames) -> Element<Message> {
|
fn create_game_icon(&self, game: &PossibleGames) -> Element<Message> {
|
||||||
let icon = self.icon_images.get(game).unwrap();
|
if let Some(icon) = self.icon_images.get(game) {
|
||||||
let img = if &self.selected_game == game {
|
let img = if &self.selected_game == game {
|
||||||
image(icon.clone().into_handle())
|
image(icon.clone().into_handle())
|
||||||
.width(Length::Fixed(52.0))
|
.width(Length::Fixed(52.0))
|
||||||
.height(Length::Fixed(68.0))
|
.height(Length::Fixed(68.0))
|
||||||
} else {
|
} else {
|
||||||
image(icon.brighten(-85).blur(2.5).adjust_contrast(-15.0).into_handle())
|
image(icon.brighten(-85).blur(2.5).adjust_contrast(-15.0).into_handle())
|
||||||
.width(Length::Fixed(48.0))
|
.width(Length::Fixed(48.0))
|
||||||
.height(Length::Fixed(64.0))
|
.height(Length::Fixed(64.0))
|
||||||
};
|
};
|
||||||
|
|
||||||
mouse_area(
|
mouse_area(
|
||||||
img
|
img
|
||||||
)
|
)
|
||||||
// .on_press(Message::GameSelected(game.clone()))
|
.on_press(Message::GameSelected(game.clone()))
|
||||||
.interaction(Interaction::Pointer)
|
.interaction(Interaction::Pointer)
|
||||||
.into()
|
.into()
|
||||||
|
} else {
|
||||||
|
text("loading").size(10).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn get_splash(&self) -> Option<Element<Message>> {
|
||||||
|
if let Some(splash) = self.splash_images.get(&self.selected_game) {
|
||||||
|
let preferred_size = self.selected_game.get_game_preferred_size();
|
||||||
|
Some(image(splash.clone().into_handle())
|
||||||
|
.width(Length::Fixed(preferred_size.0 as f32))
|
||||||
|
.height(Length::Fixed(preferred_size.1 as f32)).into())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +184,6 @@ impl From<SavedState> for Box<State> {
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum LoadError {
|
enum LoadError {
|
||||||
File,
|
|
||||||
Format,
|
Format,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,8 +196,12 @@ enum SaveError {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
enum Message {
|
enum Message {
|
||||||
EventOccurred(Event),
|
EventOccurred(Event),
|
||||||
DragStarted(),
|
DragWindow,
|
||||||
// GameSelected(PossibleGames),
|
LoadIcons(HashMap<PossibleGames, DynamicImage>),
|
||||||
|
LoadSplashes(HashMap<PossibleGames, DynamicImage>),
|
||||||
|
LoadBackground(()),
|
||||||
|
RefreshInstall(()),
|
||||||
|
GameSelected(PossibleGames),
|
||||||
Close,
|
Close,
|
||||||
Minimize
|
Minimize
|
||||||
}
|
}
|
||||||
|
@ -181,19 +215,21 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(self) -> Result<State, LoadError> {
|
fn load(self) -> Result<State, LoadError> {
|
||||||
let contents = read_to_string(Self::path()).map_err(|_| LoadError::File)?;
|
if let Ok(contents) = read_to_string(Self::path()) {
|
||||||
|
let saved_state: SavedState = serde_json::from_str(&contents).map_err(|_| LoadError::Format)?;
|
||||||
|
|
||||||
let saved_state: SavedState =
|
Ok(State {
|
||||||
serde_json::from_str(&contents).map_err(|_| LoadError::Format)?;
|
selected_game: saved_state.selected_game,
|
||||||
|
installed_games: saved_state.installed_games,
|
||||||
Ok(State {
|
installed_game_servers: saved_state.installed_game_servers,
|
||||||
selected_game: saved_state.selected_game,
|
db_software_installed: saved_state.db_software_installed,
|
||||||
installed_games: saved_state.installed_games,
|
background: self.background,
|
||||||
installed_game_servers: saved_state.installed_game_servers,
|
splash_images: self.splash_images,
|
||||||
db_software_installed: saved_state.db_software_installed,
|
icon_images: self.icon_images,
|
||||||
background: self.background,
|
})
|
||||||
icon_images: self.icon_images
|
} else {
|
||||||
})
|
Ok(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save(&mut self) -> Result<(), SaveError> {
|
fn save(&mut self) -> Result<(), SaveError> {
|
||||||
|
@ -222,22 +258,29 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_icons() -> HashMap<PossibleGames, DynamicImage> {
|
||||||
|
let mut icons: HashMap<PossibleGames, DynamicImage> = HashMap::new();
|
||||||
|
for game in PossibleGames::iter() {
|
||||||
|
let icon = get_game_icon_dynamic_image(&game);
|
||||||
|
icons.insert(game, icon);
|
||||||
|
};
|
||||||
|
icons
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_splashes() -> HashMap<PossibleGames, DynamicImage> {
|
||||||
|
let mut splashes: HashMap<PossibleGames, DynamicImage> = HashMap::new();
|
||||||
|
for game in PossibleGames::iter() {
|
||||||
|
let splash = get_game_splash_dynamic_image(&game);
|
||||||
|
if let Some(splash) = splash {splashes.insert(game, splash)} else {continue};
|
||||||
|
};
|
||||||
|
splashes
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn empty() {}
|
||||||
|
|
||||||
impl Launcher {
|
impl Launcher {
|
||||||
fn boot() -> (Self, Task<Message>) {
|
fn boot() -> (Self, Task<Message>) {
|
||||||
refresh_install().unwrap();
|
(Self::Loaded(Box::new(State::default().load().unwrap())), Task::batch([Task::perform(empty(), Message::LoadBackground), Task::perform(get_icons(), Message::LoadIcons), Task::perform(get_splashes(), Message::LoadSplashes), Task::perform(empty(), Message::RefreshInstall)]))
|
||||||
let launcher_bg = get_game_background(&PossibleGames::default());
|
|
||||||
let mut icons = HashMap::new();
|
|
||||||
for game in PossibleGames::iter() {
|
|
||||||
let icon = get_game_icon_dynamic_image(&game);
|
|
||||||
icons.insert(game, icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
let final_state = State {
|
|
||||||
background: Some(launcher_bg),
|
|
||||||
icon_images: icons,
|
|
||||||
..State::default()
|
|
||||||
};
|
|
||||||
(Self::Loaded(Box::new(final_state)), Task::none())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn title(&self) -> String {
|
fn title(&self) -> String {
|
||||||
|
@ -248,7 +291,7 @@ impl Launcher {
|
||||||
match self {
|
match self {
|
||||||
Launcher::Loaded(state) => {
|
Launcher::Loaded(state) => {
|
||||||
match message {
|
match message {
|
||||||
Message::DragStarted() => {
|
Message::DragWindow => {
|
||||||
window::get_latest().and_then(move |id: window::Id| {
|
window::get_latest().and_then(move |id: window::Id| {
|
||||||
window::drag(id)
|
window::drag(id)
|
||||||
})
|
})
|
||||||
|
@ -270,8 +313,34 @@ impl Launcher {
|
||||||
} else {
|
} else {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
_ => Task::none()
|
Message::LoadSplashes(splashes) => {
|
||||||
|
state.splash_images = splashes;
|
||||||
|
Task::none()
|
||||||
|
},
|
||||||
|
Message::LoadBackground(()) => {
|
||||||
|
state.background = Some(get_game_background(&PossibleGames::default()));
|
||||||
|
let (width, height) = state.selected_game.get_game_preferred_size();
|
||||||
|
window::get_latest().and_then(move |id: window::Id| {
|
||||||
|
window::resize(id, Size { width: width as f32, height: height as f32 })
|
||||||
|
})
|
||||||
|
},
|
||||||
|
Message::LoadIcons(icons) => {
|
||||||
|
state.icon_images = icons;
|
||||||
|
Task::none()
|
||||||
|
},
|
||||||
|
Message::RefreshInstall(_result) => {
|
||||||
|
refresh_install().unwrap();
|
||||||
|
Task::none()
|
||||||
|
},
|
||||||
|
Message::GameSelected(game) => {
|
||||||
|
state.background = Some(get_game_background(&game));
|
||||||
|
state.selected_game = game;
|
||||||
|
let (width, height) = state.selected_game.get_game_preferred_size();
|
||||||
|
window::get_latest().and_then(move |id: window::Id| {
|
||||||
|
window::resize(id, Size { width: width as f32, height: height as f32 })
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,7 +390,7 @@ impl Launcher {
|
||||||
background: Some(Color::from_rgba8(0, 0, 0, 0.75).into()),
|
background: Some(Color::from_rgba8(0, 0, 0, 0.75).into()),
|
||||||
..container::Style::default()
|
..container::Style::default()
|
||||||
}))
|
}))
|
||||||
.on_press(Message::DragStarted())
|
.on_press(Message::DragWindow)
|
||||||
.interaction(Interaction::Grab)
|
.interaction(Interaction::Grab)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -350,9 +419,21 @@ impl Launcher {
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill);
|
.height(Length::Fill);
|
||||||
|
|
||||||
let bg_element: Element<Message> = state.get_background_element();
|
let bg_element = if let Some(bg) = state.get_background_element() {
|
||||||
|
bg
|
||||||
|
} else {
|
||||||
|
center(text("Loading...").size(50)).style(|_| {container::Style { text_color: Some(Color::from_rgba8(255, 255, 255, 1.0)), background: Some(Color::from_rgba8(0, 0, 0, 1.0).into()), ..container::Style::default() }}).into()
|
||||||
|
};
|
||||||
|
|
||||||
stack![bg_element, user_area].into()
|
let mut final_stack = stack![bg_element];
|
||||||
|
|
||||||
|
if let Some(splash) = state.get_splash() {
|
||||||
|
final_stack = final_stack.push(splash);
|
||||||
|
}
|
||||||
|
|
||||||
|
final_stack = final_stack.push(user_area);
|
||||||
|
|
||||||
|
final_stack.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,9 +44,9 @@ pub fn get_game_background(game: &PossibleGames) -> LauncherBackground {
|
||||||
fn get_background_file(proj_dirs: &ProjectDirs, game: &PossibleGames) -> Result<Vec<u8>, std::io::Error> {
|
fn get_background_file(proj_dirs: &ProjectDirs, game: &PossibleGames) -> Result<Vec<u8>, std::io::Error> {
|
||||||
let game_dir = match game {
|
let game_dir = match game {
|
||||||
PossibleGames::WutheringWaves => proj_dirs.data_dir().join("kuro/wuwa"),
|
PossibleGames::WutheringWaves => proj_dirs.data_dir().join("kuro/wuwa"),
|
||||||
PossibleGames::ZenlessZoneZero => proj_dirs.data_dir().join("zzz"),
|
PossibleGames::ZenlessZoneZero => proj_dirs.data_dir().join("hoyoverse/zzz"),
|
||||||
PossibleGames::HonkaiStarRail => proj_dirs.data_dir().join("hsr"),
|
PossibleGames::HonkaiStarRail => proj_dirs.data_dir().join("hoyoverse/hsr"),
|
||||||
PossibleGames::GenshinImpact => proj_dirs.data_dir().join("gi"),
|
PossibleGames::GenshinImpact => proj_dirs.data_dir().join("hoyoverse/gi"),
|
||||||
};
|
};
|
||||||
|
|
||||||
if !game_dir.exists() {
|
if !game_dir.exists() {
|
||||||
|
@ -73,6 +73,20 @@ fn get_background_file(proj_dirs: &ProjectDirs, game: &PossibleGames) -> Result<
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_game_splash_dynamic_image(game: &PossibleGames) -> Option<DynamicImage> {
|
||||||
|
let proj_dirs = ProjectDirs::from("com", "RabbyDevs", "rr-launcher").unwrap();
|
||||||
|
let file_data = get_splash_file(&proj_dirs, game);
|
||||||
|
if let Some(data) = file_data {
|
||||||
|
let data_cursor = Cursor::new(data);
|
||||||
|
|
||||||
|
Some(ImageReader::new(data_cursor)
|
||||||
|
.with_guessed_format()
|
||||||
|
.unwrap()
|
||||||
|
.decode()
|
||||||
|
.unwrap())
|
||||||
|
} else {None}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_game_icon_dynamic_image(game: &PossibleGames) -> DynamicImage {
|
pub fn get_game_icon_dynamic_image(game: &PossibleGames) -> DynamicImage {
|
||||||
let proj_dirs = ProjectDirs::from("com", "RabbyDevs", "rr-launcher").unwrap();
|
let proj_dirs = ProjectDirs::from("com", "RabbyDevs", "rr-launcher").unwrap();
|
||||||
let file_data: &[u8] = match game {
|
let file_data: &[u8] = match game {
|
||||||
|
@ -118,6 +132,29 @@ fn get_hoyo_game_icon_file(proj_dirs: &ProjectDirs, game: &PossibleGames) -> Vec
|
||||||
std::fs::read(icon.expect("installation went wrong.").path()).unwrap()
|
std::fs::read(icon.expect("installation went wrong.").path()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_splash_file(proj_dirs: &ProjectDirs, game: &PossibleGames) -> Option<Vec<u8>> {
|
||||||
|
let game_path = match game {
|
||||||
|
PossibleGames::ZenlessZoneZero => "hoyoverse/zzz",
|
||||||
|
PossibleGames::HonkaiStarRail => "hoyoverse/hsr",
|
||||||
|
PossibleGames::GenshinImpact => "hoyoverse/gi",
|
||||||
|
PossibleGames::WutheringWaves => "kuro/wuwa"
|
||||||
|
};
|
||||||
|
|
||||||
|
let data_dir = proj_dirs.data_dir().join(game_path);
|
||||||
|
let icon: Option<DirEntry> = {
|
||||||
|
let mut icon = None;
|
||||||
|
for path in data_dir.read_dir().unwrap() {
|
||||||
|
let path = path.unwrap();
|
||||||
|
if path.file_name().into_string().unwrap().starts_with("splash_") {
|
||||||
|
icon = Some(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
icon
|
||||||
|
};
|
||||||
|
|
||||||
|
icon.map(|icon| std::fs::read(icon.path()).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
fn rad(deg: f32) -> f32 {
|
fn rad(deg: f32) -> f32 {
|
||||||
deg * std::f32::consts::PI / 180.0
|
deg * std::f32::consts::PI / 180.0
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue