feat: new config format
This commit is contained in:
22
rust/src/modules/bepinex.rs
Normal file
22
rust/src/modules/bepinex.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use anyhow::Result;
|
||||
use ini::Ini;
|
||||
use crate::{model::config::BepInEx, profiles::ProfilePaths};
|
||||
|
||||
impl BepInEx {
|
||||
pub fn line_up(&self, p: &impl ProfilePaths) -> Result<()> {
|
||||
let dir = p.data_dir().join("BepInEx");
|
||||
|
||||
if dir.exists() && dir.is_dir() {
|
||||
let dir = dir.join("config");
|
||||
std::fs::create_dir_all(&dir)?;
|
||||
let mut ini = Ini::new();
|
||||
|
||||
ini.with_section(Some("Logging.Console"))
|
||||
.set("Enabled", if self.console { "true" } else { "false" });
|
||||
|
||||
ini.write_to_file(dir.join("BepInEx.cfg"))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
139
rust/src/modules/display.rs
Normal file
139
rust/src/modules/display.rs
Normal file
@ -0,0 +1,139 @@
|
||||
|
||||
use crate::model::config::{Display, DisplayMode};
|
||||
use anyhow::Result;
|
||||
use displayz::{query_displays, DisplaySet};
|
||||
use tauri::{AppHandle, Listener};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[derive(Clone)]
|
||||
pub struct DisplayInfo {
|
||||
pub primary: String,
|
||||
pub set: Option<DisplaySet>
|
||||
}
|
||||
|
||||
impl Default for DisplayInfo {
|
||||
fn default() -> Self {
|
||||
DisplayInfo {
|
||||
primary: "default".to_owned(),
|
||||
set: query_displays().ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Display {
|
||||
fn default() -> Self {
|
||||
Display {
|
||||
target: "default".to_owned(),
|
||||
rez: (1080, 1920),
|
||||
mode: DisplayMode::Borderless,
|
||||
rotation: 0,
|
||||
frequency: 60,
|
||||
borderless_fullscreen: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
impl Display {
|
||||
pub fn activate(&self, app: AppHandle) {
|
||||
let display = self.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
let info = display.line_up()?;
|
||||
if let Some(info) = info {
|
||||
app.listen("launch-end", move |_| {
|
||||
if let Err(e) = Self::clean_up(&info) {
|
||||
log::error!("Error cleaning up display: {:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Ok::<(), anyhow::Error>(())
|
||||
});
|
||||
}
|
||||
|
||||
fn line_up(&self) -> Result<Option<DisplayInfo>> {
|
||||
use anyhow::anyhow;
|
||||
use displayz::{query_displays, Orientation, Resolution, Frequency};
|
||||
|
||||
if self.target == "default" {
|
||||
log::debug!("prepare display: skip");
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let display_set = query_displays()?;
|
||||
|
||||
let primary = display_set
|
||||
.displays()
|
||||
.find(|display| display.is_primary())
|
||||
.ok_or_else(|| anyhow!("Primary display not found"))?;
|
||||
|
||||
let target = display_set
|
||||
.displays()
|
||||
.find(|display| display.name() == self.target)
|
||||
.ok_or_else(|| anyhow!("Display {} not found", self.target))?;
|
||||
|
||||
target.set_primary()?;
|
||||
let settings = target.settings()
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("Unable to query display settings"))?;
|
||||
|
||||
let res = DisplayInfo {
|
||||
primary: primary.name().to_owned(),
|
||||
set: Some(display_set.clone())
|
||||
};
|
||||
|
||||
if self.rotation == 90 || self.rotation == 270 {
|
||||
let rez = settings.borrow_mut().resolution;
|
||||
settings.borrow_mut().orientation = if self.rotation == 90 { Orientation::PortraitFlipped } else { Orientation::Portrait };
|
||||
if rez.height < rez.width {
|
||||
settings.borrow_mut().resolution = Resolution::new(rez.height, rez.width);
|
||||
}
|
||||
}
|
||||
|
||||
let frequency: u32 = self.frequency
|
||||
.try_into()
|
||||
.map_err(|e| anyhow!("Invalid display frequency: {}", e))?;
|
||||
|
||||
let width: u32 = self.rez.0
|
||||
.try_into()
|
||||
.map_err(|e| anyhow!("Invalid display width: {}", e))?;
|
||||
|
||||
let height: u32 = self.rez.1
|
||||
.try_into()
|
||||
.map_err(|e| anyhow!("Invalid display height: {}", e))?;
|
||||
|
||||
settings.borrow_mut().frequency = Frequency::new(frequency);
|
||||
|
||||
if self.borderless_fullscreen && self.mode == DisplayMode::Borderless {
|
||||
settings.borrow_mut().resolution = Resolution::new(width, height);
|
||||
}
|
||||
|
||||
display_set.apply()?;
|
||||
displayz::refresh()?;
|
||||
|
||||
log::debug!("prepare display: done");
|
||||
|
||||
Ok(Some(res))
|
||||
}
|
||||
|
||||
fn clean_up(info: &DisplayInfo) -> Result<()> {
|
||||
use anyhow::anyhow;
|
||||
|
||||
let display_set = info.set.as_ref()
|
||||
.ok_or_else(|| anyhow!("Unable to clean up displays: no display set"))?;
|
||||
|
||||
let primary = display_set
|
||||
.displays()
|
||||
.find(|display| display.name() == info.primary)
|
||||
.ok_or_else(|| anyhow!("Display {} not found", info.primary))?;
|
||||
|
||||
primary.set_primary()?;
|
||||
|
||||
display_set.apply()?;
|
||||
displayz::refresh()?;
|
||||
|
||||
log::debug!("undo display: done");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
5
rust/src/modules/mod.rs
Normal file
5
rust/src/modules/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
pub mod display;
|
||||
pub mod package;
|
||||
pub mod segatools;
|
||||
pub mod network;
|
||||
pub mod bepinex;
|
67
rust/src/modules/network.rs
Normal file
67
rust/src/modules/network.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use std::{path::PathBuf, process::Command};
|
||||
use yaml_rust2::YamlLoader;
|
||||
use anyhow::{Result, anyhow};
|
||||
use ini::Ini;
|
||||
use crate::model::config::{Network, NetworkType};
|
||||
|
||||
impl Network {
|
||||
pub fn line_up(&self, ini: &mut Ini) -> Result<()> {
|
||||
log::debug!("begin line-up: network");
|
||||
|
||||
ini.with_section(Some("dns")).set("default", &self.remote_address);
|
||||
|
||||
let mut section_netenv = ini.with_section(Some("netenv"));
|
||||
|
||||
section_netenv.set("enable", "1");
|
||||
|
||||
if let Some(suffix) = self.suffix {
|
||||
section_netenv.set("addrSuffix", suffix.to_string());
|
||||
}
|
||||
|
||||
let mut section_keychip = ini.with_section(Some("keychip"));
|
||||
|
||||
if self.subnet.len() > 0 {
|
||||
section_keychip.set("subnet", &self.subnet);
|
||||
}
|
||||
|
||||
if self.network_type == NetworkType::Artemis {
|
||||
let network_path = PathBuf::from(&self.local_path);
|
||||
let artemis_dir = network_path.parent()
|
||||
.ok_or_else(|| anyhow!("Invalid ARTEMiS path {}", &self.local_path))?;
|
||||
let cfg_path = artemis_dir.join("config").join("core.yaml");
|
||||
|
||||
let cfg = std::fs::read_to_string(&cfg_path)
|
||||
.map_err(|e| anyhow!("Unable to open core.yaml: {}", e))?;
|
||||
let cfg = YamlLoader::load_from_str(&cfg)
|
||||
.map_err(|e| anyhow!("Unable to read core.yaml: {}", e))?;
|
||||
let cfg = &cfg[0];
|
||||
log::debug!("{:?}", cfg);
|
||||
let hostname = &cfg["server"]["hostname"];
|
||||
let hostname = hostname.clone().into_string();
|
||||
if let Some(hostname) = hostname {
|
||||
ini.with_section(Some("dns")).set("default", hostname);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let mut cmd = Command::new("cmd.exe");
|
||||
|
||||
cmd.arg("/C");
|
||||
|
||||
if self.local_console == true {
|
||||
cmd.arg("start");
|
||||
}
|
||||
cmd.args(["python", &self.local_path]);
|
||||
cmd.current_dir(artemis_dir);
|
||||
cmd.spawn()
|
||||
.map_err(|e| anyhow!("Unable to spawn artemis: {}", e))?;
|
||||
} else {
|
||||
log::warn!("unable to parse the artemis hostname");
|
||||
}
|
||||
} else if self.keychip.len() > 0 {
|
||||
section_keychip.set("id", &self.keychip);
|
||||
}
|
||||
|
||||
log::debug!("end line-up: network");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
43
rust/src/modules/package.rs
Normal file
43
rust/src/modules/package.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use anyhow::Result;
|
||||
use std::collections::BTreeSet;
|
||||
use crate::pkg::PkgKey;
|
||||
use crate::util;
|
||||
use crate::profiles::ProfilePaths;
|
||||
|
||||
pub async fn prepare_packages<'a>(p: &'a impl ProfilePaths, pkgs: &BTreeSet<PkgKey>) -> Result<()> {
|
||||
log::debug!("begin prepare packages");
|
||||
|
||||
let pfx_dir = p.data_dir();
|
||||
let opt_dir = pfx_dir.join("option");
|
||||
|
||||
if pfx_dir.join("BepInEx").exists() {
|
||||
tokio::fs::remove_dir_all(pfx_dir.join("BepInEx")).await?;
|
||||
}
|
||||
|
||||
if !opt_dir.exists() {
|
||||
tokio::fs::create_dir(opt_dir).await?;
|
||||
}
|
||||
|
||||
for m in pkgs {
|
||||
log::debug!("preparing {}", m);
|
||||
let (namespace, name) = m.0.split_at(m.0.find("-").expect("Invalid mod definition"));
|
||||
let bpx_dir = util::pkg_dir_of(namespace, &name[1..]) // cut the hyphen
|
||||
.join("app")
|
||||
.join("BepInEx");
|
||||
if bpx_dir.exists() {
|
||||
util::copy_recursive(&bpx_dir, &pfx_dir.join("BepInEx"))?;
|
||||
}
|
||||
|
||||
let opt_dir = util::pkg_dir_of(namespace, &name[1..]).join("option");
|
||||
if opt_dir.exists() {
|
||||
let x = opt_dir.read_dir().unwrap().next().unwrap()?;
|
||||
if x.metadata()?.is_dir() {
|
||||
util::symlink(&x.path(), &pfx_dir.join("option").join(x.file_name())).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("end prepare packages");
|
||||
|
||||
Ok(())
|
||||
}
|
93
rust/src/modules/segatools.rs
Normal file
93
rust/src/modules/segatools.rs
Normal file
@ -0,0 +1,93 @@
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use ini::Ini;
|
||||
use crate::{model::{config::Segatools, segatools_base::segatools_base}, profiles::ProfilePaths, util::{self, PathStr}};
|
||||
|
||||
impl Default for Segatools {
|
||||
fn default() -> Self {
|
||||
Segatools {
|
||||
target: PathBuf::default(),
|
||||
amfs: PathBuf::default(),
|
||||
option: PathBuf::default(),
|
||||
appdata: PathBuf::from("appdata"),
|
||||
enable_aime: false,
|
||||
intel: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Segatools {
|
||||
pub async fn line_up(&self, p: &impl ProfilePaths) -> 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() {
|
||||
tokio::fs::write(&ini_path, segatools_base()).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()?);
|
||||
|
||||
ini_out.with_section(Some("unity"))
|
||||
.set("enable", "1")
|
||||
.set(
|
||||
"targetAssembly",
|
||||
pfx_dir.join("BepInEx").join("core").join("BepInEx.Preloader.dll").stringify()?
|
||||
);
|
||||
|
||||
if self.enable_aime {
|
||||
ini_out.with_section(Some("aime"))
|
||||
.set("enable", "1")
|
||||
.set("aimePath", p.config_dir().join("aime.txt").stringify()?);
|
||||
} else {
|
||||
ini_out.with_section(Some("aime"))
|
||||
.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?;
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!("end line-up: segatools");
|
||||
|
||||
Ok(ini_out)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user