use std::path::{PathBuf, Path}; use anyhow::{anyhow, Result}; use ini::Ini; use crate::{model::{misc::{ConfigHook, ConfigHookAime, ConfigHookAimeUnit, ConfigHookAuth, Game}, profile::{Aime, IOSelection, Segatools}}, profiles::ProfilePaths, util::{self, PathStr}}; use crate::pkg_store::PackageStore; impl Segatools { pub fn fix(&mut self, store: &PackageStore) { macro_rules! remove_if_nonpresent { ($item:expr,$key:expr,$emptyval:expr,$store:expr) => { if let Ok(pkg) = $store.get($key) { if pkg.loc.is_none() { $item = $emptyval; } } else { $item = $emptyval; } } } if let Some(key) = &self.hook { remove_if_nonpresent!(self.hook, key, None, store); } if let IOSelection::Custom(key) = &self.io2 { remove_if_nonpresent!(self.io2, key, IOSelection::default(), store); } match &self.aime { Aime::AMNet(key) => remove_if_nonpresent!(self.aime, key, Aime::BuiltIn, store), Aime::Other(key) => remove_if_nonpresent!(self.aime, key, Aime::BuiltIn, store), _ => {}, } } pub fn load_from_ini(&mut self, ini: &Ini, config_dir: impl AsRef) -> Result<()> { log::debug!("loading sgt"); if let Some(s) = ini.section(Some("vfs")) { s.get("amfs").map(|v| self.amfs = PathBuf::from(v)); s.get("appdata").map(|v| self.appdata = PathBuf::from(v)); s.get("option").map(|v| self.option = PathBuf::from(v)); } if let Some(s) = ini.section(Some("aime")) { if s.get("enable").unwrap_or("0") == "1" { if let Some(aime_path) = s.get("aimePath") { if let Some(game_dir) = self.target.parent() { let target = game_dir.join(aime_path); std::fs::copy(target, config_dir.as_ref().join("aime.txt"))?; } else { log::error!("profile doesn't have a game directory"); } } else { log::warn!("aime emulation is enabled, but no aimePath specified"); } } } Ok(()) } pub async fn line_up(&self, p: &impl ProfilePaths, game: Game) -> Result { log::debug!("begin line-up: segatools"); let pfx_dir = p.data_dir(); let exe_dir = self.target.parent().ok_or_else(|| anyhow!("Invalid target path"))?; log::debug!("segatools: {:?} {:?}", pfx_dir, exe_dir); let ini_path = p.config_dir().join("segatools-base.ini"); if !ini_path.exists() { match game { Game::Ongeki => tokio::fs::write(&ini_path, include_bytes!("../../static/segatools-ongeki.ini")) .await.map_err(|e| anyhow!("Error creating {:?}: {}", ini_path, e))?, Game::Chunithm => tokio::fs::write(&ini_path, include_bytes!("../../static/segatools-chunithm.ini")) .await.map_err(|e| anyhow!("Error creating {:?}: {}", ini_path, e))? } } if !pfx_dir.exists() { tokio::fs::create_dir(&pfx_dir).await .map_err(|e| anyhow!("Error creating {:?}: {}", pfx_dir, e))?; } let ini_in = tokio::fs::read_to_string(&ini_path).await?; let ini_in = Ini::load_from_str(&ini_in)?; let opt_dir_out = &pfx_dir.join("option"); let opt_dir_in = if self.option.as_os_str().len() > 0 && self.option.is_relative() { exe_dir.join(&self.option) } else { self.option.clone() }; let mut ini_out = ini_in.clone(); ini_out.with_section(Some("vfs")) .set( "option", opt_dir_out.stringify()? ) .set("amfs", self.amfs.stringify()?) .set("appdata", self.appdata.stringify()?); if game == Game::Ongeki { ini_out.with_section(Some("unity")) .set("enable", "1") .set( "targetAssembly", pfx_dir.join("BepInEx").join("core").join("BepInEx.Preloader.dll").stringify()? ); } if self.aime != Aime::Disabled { ini_out.with_section(Some("aime")) .set("enable", "1") .set("aimePath", p.config_dir().join("aime.txt").stringify()?); if let Aime::AMNet(key) = &self.aime { let mut aimeio = ini_out.with_section(Some("aimeio")); aimeio .set("path", util::pkg_dir().join(key.to_string()).join("segatools").join("aimeio.dll").stringify()?) .set("serverAddress", &self.amnet.addr) .set("useAimeDBForPhysicalCards", if self.amnet.physical { "1" } else { "0" }) .set("enableKeyboardMode", "0"); match game { Game::Ongeki => aimeio.set("gameId", "SDDT"), Game::Chunithm => aimeio.set("gameId", "SDHD") }; if let Ok(keyboard_code) = std::fs::read_to_string(p.config_dir().join("aime.txt")) { log::debug!("{} {}", keyboard_code, keyboard_code.len()); if keyboard_code.len() == 20 { aimeio.set("enableKeyboardMode", "1"); } } if self.amnet.name.len() > 0 { aimeio.set("serverName", &self.amnet.name); } } } else { ini_out.with_section(Some("aime")) .set("enable", "0"); } if game == Game::Ongeki { if let IOSelection::Custom(io) = &self.io2 { ini_out.with_section(Some("mu3io")) .set("path", util::pkg_dir().join(io.to_string()).join("segatools").join("mu3io.dll").stringify()?); } else { ini_out.with_section(Some("mu3io")) .set("path", ""); } } match game { Game::Ongeki => { match &self.io2 { IOSelection::Custom(io) => { ini_out.with_section(Some("mu3io")) .set("path", util::pkg_dir().join(io.to_string()).join("segatools").join("mu3io.dll").stringify()?); } IOSelection::SegatoolsBuiltIn => { ini_out.with_section(Some("mu3io")) .set("path", ""); } IOSelection::Hardware => { ini_out.with_section(Some("io4")) .set("enable", "0"); } } }, Game::Chunithm => { match &self.io2 { IOSelection::Custom(io) => { ini_out.with_section(Some("chuniio")) .set("path32", util::pkg_dir().join(io.to_string()).join("segatools").join("chuniio32.dll").stringify()?) .set("path64", util::pkg_dir().join(io.to_string()).join("segatools").join("chuniio64.dll").stringify()?); } IOSelection::SegatoolsBuiltIn => { ini_out.with_section(Some("chuniio")) .set("path32", "") .set("path64", ""); } IOSelection::Hardware => { ini_out.with_section(Some("io4")) .set("enable", "0"); } } } }; log::debug!("option dir: {:?} -> {:?}", opt_dir_in, opt_dir_out); if !opt_dir_out.exists() { tokio::fs::create_dir(opt_dir_out).await?; } if opt_dir_in.as_os_str().len() > 0 { for opt in opt_dir_in.read_dir()? { let opt = opt?; util::symlink(&opt.path(), opt_dir_out.join(opt.file_name())).await?; } } if !exe_dir.join("DEVICE").join("ca.crt").exists() { util::copy_directory( util::pkg_dir().join(self.hook_dir()?).join("DEVICE"), exe_dir.join("DEVICE"), false )?; } let mut cfg_hook = ConfigHook::default(); if game == Game::Chunithm { cfg_hook.allnet_auth = Some({ ConfigHookAuth { r#type: "1.0".to_owned() } }) } if let Some(port) = self.aime_port { if self.aime == Aime::Disabled { cfg_hook.aime = Some({ ConfigHookAime { unit: vec![ ConfigHookAimeUnit { port, id: 1 } ] } }) } } std::fs::write(pfx_dir.join("config_hook.json"), serde_json::to_string(&cfg_hook)?)?; log::debug!("end line-up: segatools"); Ok(ini_out) } pub fn hook_dir(&self) -> Result { Ok(util::pkg_dir() .join(self.hook.as_ref().ok_or_else(|| anyhow!("No hook"))?.to_string()) .join("segatools")) } }