diff --git a/bins/ayaka-check/src/main.rs b/bins/ayaka-check/src/main.rs index 0a5eb6b1..1786e33e 100644 --- a/bins/ayaka-check/src/main.rs +++ b/bins/ayaka-check/src/main.rs @@ -9,7 +9,8 @@ use std::{ #[derive(Debug, Parser)] #[clap(about, version, author)] pub struct Options { - input: OsString, + #[clap(required = true)] + input: Vec, #[clap(long)] auto: bool, #[clap(short, long)] diff --git a/bins/ayaka-gui/src-tauri/src/main.rs b/bins/ayaka-gui/src-tauri/src/main.rs index bc50a5e9..18ecc452 100644 --- a/bins/ayaka-gui/src-tauri/src/main.rs +++ b/bins/ayaka-gui/src-tauri/src/main.rs @@ -58,7 +58,7 @@ fn ayaka_version() -> &'static str { #[derive(Debug, Clone, Serialize)] #[serde(tag = "t", content = "data")] enum OpenGameStatus { - LoadProfile(String), + LoadProfile, CreateRuntime, LoadPlugin(String, usize, usize), GamePlugin, @@ -78,7 +78,7 @@ impl OpenGameStatus { #[derive(Default)] struct Storage { - config: String, + config: Vec, dist_port: u16, manager: FileSettingsManager, records: Mutex>, @@ -89,9 +89,9 @@ struct Storage { } impl Storage { - pub fn new(resolver: &PathResolver, config: impl Into, dist_port: u16) -> Self { + pub fn new(resolver: &PathResolver, config: Vec, dist_port: u16) -> Self { Self { - config: config.into(), + config, dist_port, manager: FileSettingsManager::new(resolver), ..Default::default() @@ -129,7 +129,7 @@ async fn open_game(handle: AppHandle, storage: State<'_, Storage>) -> CommandRes while let Some(status) = context.next().await { match status { OpenStatus::LoadProfile => { - OpenGameStatus::LoadProfile(config.clone()).emit(&handle)?; + OpenGameStatus::LoadProfile.emit(&handle)?; } OpenStatus::CreateRuntime => OpenGameStatus::CreateRuntime.emit(&handle)?, OpenStatus::LoadPlugin(name, i, len) => { @@ -465,22 +465,41 @@ fn main() -> Result<()> { let window = app.get_window("main").unwrap(); window.open_devtools(); } + + use serde_json::Value; + let matches = app.get_cli_matches()?; - let config = matches.args["config"] - .value - .as_str() - .map(|s| s.to_string()) - .unwrap_or_else(|| { + let config = match &matches.args["config"].value { + Value::String(s) => vec![s.to_string()], + Value::Array(arr) => arr + .iter() + .filter_map(|v| v.as_str()) + .map(|s| s.to_string()) + .collect::>(), + _ => { let current = std::env::current_exe().unwrap(); let current = current.parent().unwrap(); + let mut paths = vec![]; + let data = current.join("data.frfs"); - let data = if data.exists() { - data + if data.exists() { + paths.push(data); + paths.extend( + ('a'..'z') + .into_iter() + .map(|c| current.join(format!("data.{}.frfs", c))) + .filter(|p| p.exists()), + ); } else { - current.join("config.yaml") - }; - data.to_string_lossy().into_owned() - }); + paths.push(current.join("config.yaml")); + } + + paths + .into_iter() + .map(|p| p.to_string_lossy().into_owned()) + .collect() + } + }; app.manage(Storage::new(&resolver, config, port)); Ok(()) }) diff --git a/bins/ayaka-gui/src-tauri/tauri.conf.json b/bins/ayaka-gui/src-tauri/tauri.conf.json index 4512f176..961a5a5e 100644 --- a/bins/ayaka-gui/src-tauri/tauri.conf.json +++ b/bins/ayaka-gui/src-tauri/tauri.conf.json @@ -78,7 +78,8 @@ { "name": "config", "index": 1, - "takesValue": true + "takesValue": true, + "multiple": true } ] } diff --git a/bins/ayaka-gui/src/views/StartView.vue b/bins/ayaka-gui/src/views/StartView.vue index 8ec73549..07ebb1c6 100644 --- a/bins/ayaka-gui/src/views/StartView.vue +++ b/bins/ayaka-gui/src/views/StartView.vue @@ -65,7 +65,7 @@ export default { const t = OpenGameStatusType[s.t] switch (t) { case OpenGameStatusType.LoadProfile: - return [`Loading profile "${s.data as unknown as string}"...`, step * (t + 1)] + return [`Loading profile...`, step * (t + 1)] case OpenGameStatusType.CreateRuntime: return ["Creating runtime...", step * (t + 1)] case OpenGameStatusType.LoadPlugin: diff --git a/bins/ayaka-latex/src/main.rs b/bins/ayaka-latex/src/main.rs index 4deab863..d345ff20 100644 --- a/bins/ayaka-latex/src/main.rs +++ b/bins/ayaka-latex/src/main.rs @@ -9,7 +9,8 @@ use writer::LaTeXWriter; #[derive(Debug, Parser)] #[clap(about, version, author)] pub struct Options { - input: OsString, + #[clap(required = true)] + input: Vec, #[clap(short, long)] output: OsString, #[clap(short, long)] diff --git a/utils/ayaka-runtime/src/context.rs b/utils/ayaka-runtime/src/context.rs index 9c715dad..95c0e50c 100644 --- a/utils/ayaka-runtime/src/context.rs +++ b/utils/ayaka-runtime/src/context.rs @@ -10,6 +10,7 @@ use frfs::FRFS; use log::error; use std::{collections::HashMap, path::Path, sync::Arc}; use stream_future::stream; +use tryiterator::TryIteratorExt; use trylog::macros::*; use vfs::*; @@ -48,23 +49,40 @@ pub enum OpenStatus { impl Context { /// Open a config file with frontend type. + /// + /// If the input `paths` contains only one element, it may be a YAML or an FRFS file. + /// If the input `paths` contains many element, they should all be FRFS files, + /// and the latter one will override the former one. #[stream(OpenStatus, lifetime = "'a")] - pub async fn open<'a>(path: impl AsRef + 'a, frontend: FrontendType) -> Result { - let path = path.as_ref(); + pub async fn open<'a>(paths: &'a [impl AsRef], frontend: FrontendType) -> Result { + if paths.is_empty() { + bail!("At least one path should be input."); + } yield OpenStatus::LoadProfile; - let ext = path.extension().unwrap_or_default(); - let (root_path, filename) = if ext == "yaml" { - let root_path = path - .parent() - .ok_or_else(|| anyhow!("Cannot get parent from input path."))?; - ( - VfsPath::from(PhysicalFS::new(root_path)), - path.file_name().unwrap_or_default().to_string_lossy(), - ) - } else if ext == "frfs" { - (FRFS::new(path)?.into(), "config.yaml".into()) + let (root_path, filename) = if paths.len() == 1 { + let path = paths[0].as_ref(); + let ext = path.extension().unwrap_or_default(); + if ext == "yaml" { + let root_path = path + .parent() + .ok_or_else(|| anyhow!("Cannot get parent from input path."))?; + ( + VfsPath::from(PhysicalFS::new(root_path)), + path.file_name().unwrap_or_default().to_string_lossy(), + ) + } else if ext == "frfs" { + (FRFS::new(path)?.into(), "config.yaml".into()) + } else { + bail!("Cannot determine filesystem.") + } } else { - bail!("Cannot determine filesystem.") + let files = paths + .iter() + .rev() + .map(|path| FRFS::new(path.as_ref())) + .try_filter_map(|fs| Ok(Some(VfsPath::from(fs)))) + .try_collect::>()?; + (OverlayFS::new(&files).into(), "config.yaml".into()) }; let file = root_path.join(&filename)?.read_to_string()?; let mut config: GameConfig = serde_yaml::from_str(&file)?;