forked from akanyan/STARTLINER
114 lines
3.5 KiB
Rust
114 lines
3.5 KiB
Rust
use anyhow::{Result, anyhow};
|
|
use tokio::fs;
|
|
use std::path::{Path, PathBuf};
|
|
use ini::Ini;
|
|
use crate::util;
|
|
use crate::profile::Profile;
|
|
|
|
#[cfg(target_os = "linux")]
|
|
async fn symlink(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Result<()> {
|
|
fs::symlink(src, dst).await
|
|
}
|
|
|
|
#[cfg(target_os = "windows")]
|
|
async fn symlink(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Result<()> {
|
|
//std::os::windows::fs::junction_point(src, dst) // is unstable
|
|
junction::create(src, dst)
|
|
}
|
|
|
|
pub async fn line_up(p: &Profile, pkg_hash: String) -> Result<()> {
|
|
let dir_out = p.dir();
|
|
|
|
if dir_out.join("option").exists() {
|
|
fs::remove_dir_all(dir_out.join("option")).await?;
|
|
}
|
|
|
|
fs::create_dir_all(dir_out.join("option")).await?;
|
|
|
|
let hash_path = p.dir().join(".sl-state");
|
|
let prev_hash = fs::read_to_string(&hash_path).await.unwrap_or_default();
|
|
if prev_hash != pkg_hash {
|
|
log::debug!("state {} -> {}", prev_hash, pkg_hash);
|
|
fs::write(hash_path, pkg_hash).await
|
|
.map_err(|e| anyhow!("Unable to write the state file: {}", e))?;
|
|
prepare_packages(p).await?;
|
|
}
|
|
|
|
prepare_config(p).await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn prepare_packages(p: &Profile) -> Result<()> {
|
|
let dir_out = p.dir();
|
|
|
|
if dir_out.join("BepInEx").exists() {
|
|
fs::remove_dir_all(dir_out.join("BepInEx")).await?;
|
|
}
|
|
|
|
for m in &p.mods {
|
|
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, &dir_out.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() {
|
|
symlink(&x.path(), &dir_out.join("option").join(x.file_name())).await?;
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn prepare_config(p: &Profile) -> Result<()> {
|
|
let dir_out = p.dir();
|
|
|
|
let ini_in_raw = fs::read_to_string(p.exe_dir.join("segatools.ini")).await?;
|
|
let ini_in = Ini::load_from_str(&ini_in_raw)?;
|
|
let mut opt_dir_in = PathBuf::from(
|
|
ini_in.section(Some("vfs"))
|
|
.ok_or_else(|| anyhow!("No VFS section in segatools.ini"))?
|
|
.get("option")
|
|
.ok_or_else(|| anyhow!("No option specified in segatools.ini"))?
|
|
);
|
|
if opt_dir_in.is_relative() {
|
|
opt_dir_in = p.exe_dir.join(opt_dir_in);
|
|
}
|
|
let opt_dir_out = &dir_out.join("option");
|
|
|
|
let mut ini_out = ini_in.clone();
|
|
ini_out.with_section(Some("vfs")).set(
|
|
"option",
|
|
util::path_to_str(opt_dir_out)?
|
|
);
|
|
ini_out.with_section(Some("unity"))
|
|
.set("enable", "1")
|
|
.set(
|
|
"targetAssembly",
|
|
util::path_to_str(dir_out.join("BepInEx").join("core").join("BepInEx.Preloader.dll"))?
|
|
);
|
|
|
|
if p.get_bool("aime", false) {
|
|
ini_out.with_section(Some("aime"))
|
|
.set("enable", "1")
|
|
.set("aimePath", util::path_to_str(dir_out.join("aime.txt"))?);
|
|
}
|
|
|
|
ini_out.write_to_file(dir_out.join("segatools.ini"))?;
|
|
|
|
log::debug!("Option dir: {} -> {}", opt_dir_in.to_string_lossy(), opt_dir_out.to_string_lossy());
|
|
for opt in opt_dir_in.read_dir()? {
|
|
let opt = opt?;
|
|
symlink(&opt.path(), opt_dir_out.join(opt.file_name())).await?;
|
|
}
|
|
|
|
Ok(())
|
|
} |