Files
STARTLINER/rust/src/modules/segatools.rs
2025-04-15 13:12:12 +00:00

245 lines
9.4 KiB
Rust

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<Path>) -> 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<Ini> {
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<PathBuf> {
Ok(util::pkg_dir()
.join(self.hook.as_ref().ok_or_else(|| anyhow!("No hook"))?.to_string())
.join("segatools"))
}
}