Skip to content

Commit

Permalink
feat: enable swap for certain cores
Browse files Browse the repository at this point in the history
  • Loading branch information
goweiwen committed Jun 7, 2024
1 parent cac34ee commit c194753
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 62 deletions.
112 changes: 65 additions & 47 deletions crates/allium-launcher/src/consoles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,17 @@ use common::game_info::GameInfo;
use serde::Deserialize;

use common::constants::{ALLIUM_CONFIG_CONSOLES, ALLIUM_CONFIG_CORES, ALLIUM_RETROARCH};
use log::{debug, trace};
use log::{debug, error, trace};

use crate::entry::game::Game;

type CoreName = String;
pub type CoreName = String;

#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct Console {
/// The name of the console.
pub name: String,
/// If present, takes priority over RetroArch cores.
#[serde(default)]
pub path: Option<PathBuf>,
/// List of RetroArch cores to use. First is default.
/// List of cores to use. First is default.
#[serde(default)]
pub cores: Vec<CoreName>,
/// Folder/file names to match against. If the folder/file matches exactly OR contains a parenthesized string that matches exactly, this core will be used.
Expand All @@ -46,11 +43,25 @@ struct ConsoleConfig {

#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct Core {
/// Name of core for display.
pub name: String,
/// The kind of core: RetroArch, Path
#[serde(flatten)]
pub core: CoreType,
/// Whether swap should be enabled.
#[serde(default)]
pub swap: bool,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum CoreType {
/// Name of the RetroArch core.
RetroArch(String),
/// Path of launch script.
Path(PathBuf),
}

impl fmt::Display for Core {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.name)
Expand Down Expand Up @@ -182,46 +193,54 @@ impl ConsoleMapper {
let image = game.image().map(Path::to_path_buf);
database.increment_play_count(&game.clone().into())?;

let core = self.get_console(game.path.as_path());
Ok(if let Some(console) = core {
let game_info = if let Some(ref path) = console.path {
GameInfo::new(
game.name.clone(),
game.path.clone(),
image,
path.display().to_string(),
vec![game.path.display().to_string()],
false,
)
} else if let Some(retroarch_core) =
game.core.clone().or_else(|| console.cores.first().cloned())
{
GameInfo::new(
game.name.clone(),
game.path.clone(),
image,
if disable_savestate_auto_load {
ALLIUM_RETROARCH
.parent()
.unwrap()
.join("launch_without_savestate_auto_load.sh")
.display()
.to_string()
} else {
ALLIUM_RETROARCH.display().to_string()
},
vec![retroarch_core, game.path.display().to_string()],
true,
)
} else {
bail!("Console \"{}\" has no path or cores.", console.name);
};
debug!("Saving game info: {:?}", game_info);
game_info.save()?;
Some(Command::Exec(game_info.command()))
} else {
None
})
let console = self.get_console(game.path.as_path());
let Some(console) = console else {
bail!(
"Console for game \"{}\" does not exist.",
game.path.to_string_lossy()
);
};
let Some(core_name) = game.core.as_ref().or_else(|| console.cores.first()) else {
return Ok(None);
};
let Some(core) = self.cores.get(core_name) else {
error!("Core \"{}\" does not exist.", core_name);
return Ok(None);
};
let game_info = match &core.core {
CoreType::RetroArch(libretro_core) => GameInfo::new(
game.name.clone(),
game.path.clone(),
core_name.clone(),
image,
if disable_savestate_auto_load {
ALLIUM_RETROARCH
.parent()
.unwrap()
.join("launch_without_savestate_auto_load.sh")
.display()
.to_string()
} else {
ALLIUM_RETROARCH.display().to_string()
},
vec![libretro_core.to_string(), game.path.display().to_string()],
true,
core.swap,
),
CoreType::Path(path) => GameInfo::new(
game.name.clone(),
game.path.clone(),
core_name.clone(),
image,
path.to_string_lossy().to_string(),
vec![game.path.display().to_string()],
false,
core.swap,
),
};
debug!("Saving game info: {:?}", game_info);
game_info.save()?;
Ok(Some(Command::Exec(game_info.command())))
}

pub fn get_core_name(&self, core: &str) -> String {
Expand All @@ -246,7 +265,6 @@ mod tests {
patterns: vec!["POKE".to_string(), "PKM".to_string()],
extensions: vec!["gb".to_string(), "gbc".to_string()],
cores: vec![],
path: None,
file_name: vec![],
}];

Expand Down
7 changes: 5 additions & 2 deletions crates/allium-launcher/src/entry/game.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ use common::database::{Game as DbGame, NewGame};
use log::info;
use serde::{Deserialize, Serialize};

use crate::entry::{lazy_image::LazyImage, short_name};
use crate::{
consoles::CoreName,
entry::{lazy_image::LazyImage, short_name},
};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Game {
Expand All @@ -27,7 +30,7 @@ pub struct Game {
/// Extension of the game file.
pub extension: String,
/// The core to use for this game. If None, the default core will be used.
pub core: Option<String>,
pub core: Option<CoreName>,
/// Rating from 0 to 10, used to sort.
pub rating: Option<u8>,
/// Release date of the game, used to sort.
Expand Down
8 changes: 4 additions & 4 deletions crates/alliumd/src/alliumd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ impl AlliumDState {
}
}

fn spawn_main() -> Result<Child> {
async fn spawn_main() -> Result<Child> {
#[cfg(feature = "miyoo")]
return Ok(match GameInfo::load()? {
Some(mut game_info) => {
Expand All @@ -124,10 +124,10 @@ fn spawn_main() -> Result<Child> {
}

impl AlliumD<DefaultPlatform> {
pub fn new() -> Result<AlliumD<DefaultPlatform>> {
pub async fn new() -> Result<AlliumD<DefaultPlatform>> {
let platform = DefaultPlatform::new()?;
let state = AlliumDState::load()?;
let main = spawn_main()?;
let main = spawn_main().await?;
let locale = Locale::new(&LocaleSettings::load()?.lang);
let power_settings = PowerSettings::load()?;

Expand Down Expand Up @@ -220,7 +220,7 @@ impl AlliumD<DefaultPlatform> {
info!("main process terminated, recording play time");
self.update_play_time()?;
GameInfo::delete()?;
self.main = spawn_main()?;
self.main = spawn_main().await?;
}
}
_ = sigint.recv() => self.handle_quit().await?,
Expand Down
2 changes: 1 addition & 1 deletion crates/alliumd/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ async fn main() -> Result<()> {
console_subscriber::init();
}

let mut app = AlliumD::new()?;
let mut app = AlliumD::new().await?;
app.run_event_loop().await?;
Ok(())
}
25 changes: 21 additions & 4 deletions crates/common/src/game_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use std::{

use anyhow::Result;
use chrono::{DateTime, Duration, Utc};
use log::debug;
use serde::{Deserialize, Serialize};

use crate::constants::{ALLIUM_GAMES_DIR, ALLIUM_GAME_INFO};
use crate::constants::{ALLIUM_GAMES_DIR, ALLIUM_GAME_INFO, ALLIUM_SCRIPTS_DIR};

#[derive(Debug, Serialize, Deserialize)]
/// Information about a game. Used to restore a game after a restart, and to calculate playtime.
Expand All @@ -23,6 +24,8 @@ pub struct GameInfo {
pub args: Vec<String>,
/// Do we enable the menu? Currently only enabled if RetroArch is used.
pub has_menu: bool,
/// Whether swap should be enabled.
pub needs_swap: bool,
/// Path to the image.
pub image: Option<PathBuf>,
/// Path to the guide text file.
Expand All @@ -39,6 +42,7 @@ impl Default for GameInfo {
command: String::new(),
args: Vec::new(),
has_menu: false,
needs_swap: false,
image: None,
guide: None,
start_time: Utc::now(),
Expand All @@ -55,6 +59,7 @@ impl GameInfo {
command: String,
args: Vec<String>,
has_menu: bool,
needs_swap: bool,
) -> Self {
let guide = find_guide(&path);

Expand All @@ -64,6 +69,7 @@ impl GameInfo {
command,
args,
has_menu,
needs_swap,
image,
guide,
start_time: Utc::now(),
Expand All @@ -74,11 +80,17 @@ impl GameInfo {
pub fn load() -> Result<Option<Self>> {
Ok(if ALLIUM_GAME_INFO.exists() {
let file = File::open(ALLIUM_GAME_INFO.as_path())?;
let game_info = serde_json::from_reader(file);
if game_info.is_err() {
let Ok(game_info) = serde_json::from_reader::<_, Self>(file) else {
fs::remove_file(ALLIUM_GAME_INFO.as_path())?;
return Ok(None);
};
if game_info.needs_swap() {
debug!("enabling swap");
Command::new(ALLIUM_SCRIPTS_DIR.join("swap-on.sh"))
.spawn()?
.wait()?;
}
game_info.ok()
Some(game_info)
} else {
None
})
Expand Down Expand Up @@ -110,6 +122,11 @@ impl GameInfo {
pub fn play_time(&self) -> Duration {
Utc::now().signed_duration_since(self.start_time)
}

/// Whether swap should be enabled.
pub fn needs_swap(&self) -> bool {
self.needs_swap
}
}

/// Searches for the guide path, caches it, and returns it
Expand Down
5 changes: 2 additions & 3 deletions static/.allium/config/consoles.toml
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ extensions = ["p8", "png"]

[[consoles]]
name = "Ports Collection"
path = "/mnt/SDCARD/.allium/cores/pak/launch.sh"
cores = ["native"]
patterns = ["PORTS", "SH", "NATIVE"]
extensions = ["port"]

Expand Down Expand Up @@ -535,12 +535,11 @@ patterns = ["XRICK"]

[[consoles]]
name = "Nintendo DS"
path = "/mnt/SDCARD/.allium/cores/drastic/launch.sh"
cores = ["drastic"]
patterns = ["NDS", "DS"]
extensions = ["nds"]

[[consoles]]
name = "Movies"
path = "/mnt/SDCARD/.allium/cores/ffplay/launch.sh"
patterns = ["MEDIA", "Movies"]
extensions = ["3g2", "3gp", "aac", "avi", "flv", "m4a", "m4a", "mkv", "mj2", "mov", "mp3", "mp4", "mpeg", "ogg", "oss", "wav", "webm"]
Loading

0 comments on commit c194753

Please sign in to comment.