feat: chusanApp.exe patching

This commit is contained in:
2025-04-13 18:15:41 +00:00
parent 6270fce05f
commit 4247e19996
18 changed files with 406 additions and 187 deletions

View File

@ -1,7 +1,6 @@
use serde::{Deserialize, Serialize};
use tauri::AppHandle;
pub use types::{Profile, ProfileData, ProfileMeta, ProfilePaths, StartPayload};
use std::{collections::{BTreeMap, BTreeSet}, path::{Path, PathBuf}};
use crate::{model::{misc::Game, patch::PatchSelection, profile::{Aime, ChunithmKeyboard, Keyboard, Mu3Ini, OngekiKeyboard, ProfileModule}}, modules::package::prepare_packages, pkg::PkgKey, pkg_store::PackageStore, util};
use crate::{model::{misc::Game, patch::{PatchFileVec, PatchSelection}, profile::{Aime, ChunithmKeyboard, Keyboard, Mu3Ini, OngekiKeyboard, ProfileModule}}, modules::{display_windows::DisplayInfo, package::prepare_packages}, pkg::PkgKey, pkg_store::PackageStore, util};
use tauri::Emitter;
use std::process::Stdio;
use crate::model::profile::BepInEx;
@ -11,57 +10,7 @@ use std::fs::File;
use tokio::process::Command;
use tokio::task::JoinSet;
pub trait ProfilePaths {
fn config_dir(&self) -> PathBuf;
fn data_dir(&self) -> PathBuf;
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
pub struct ProfileMeta {
pub game: Game,
pub name: String
}
impl ProfilePaths for ProfileMeta {
fn config_dir(&self) -> PathBuf {
util::profile_config_dir(self.game, &self.name)
}
fn data_dir(&self) -> PathBuf {
util::data_dir().join(format!("profile-{}-{}", &self.game, &self.name))
}
}
#[derive(Deserialize, Serialize, Clone)]
pub struct Profile {
pub meta: ProfileMeta,
pub data: ProfileData,
}
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct ProfileData {
pub mods: BTreeSet<PkgKey>,
pub sgt: Segatools,
pub network: Network,
#[serde(skip_serializing_if = "Option::is_none")]
pub display: Option<Display>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bepinex: Option<BepInEx>,
#[cfg(not(target_os = "windows"))]
pub wine: crate::model::profile::Wine,
#[serde(skip_serializing_if = "Option::is_none")]
pub mu3_ini: Option<Mu3Ini>,
#[serde(skip_serializing_if = "Option::is_none")]
pub keyboard: Option<Keyboard>,
#[serde(skip_serializing_if = "Option::is_none")]
pub patches: Option<PatchSelection>,
}
pub mod types;
impl Profile {
pub fn new(mut meta: ProfileMeta) -> Result<Self> {
@ -205,27 +154,15 @@ impl Profile {
self.data.patches = source.patches;
}
}
pub async fn line_up(&self, pkg_hash: String, refresh: bool, _app: AppHandle) -> Result<()> {
pub fn prepare_display(&self) -> Result<Option<DisplayInfo>> {
let info = match &self.data.display {
None => None,
Some(display) => display.prepare()?
};
let res = self.line_up_the_rest(pkg_hash, refresh).await;
#[cfg(target_os = "windows")]
if let Some(info) = info {
use crate::model::profile::Display;
if res.is_ok() {
Display::wait_for_exit(_app, info);
} else {
Display::clean_up(&info)?;
}
}
res
Ok(info)
}
async fn line_up_the_rest(&self, pkg_hash: String, refresh: bool) -> Result<()> {
pub async fn line_up(&self, pkg_hash: String, refresh: bool, patch_files: &PatchFileVec) -> Result<()> {
if !self.data_dir().exists() {
tokio::fs::create_dir(self.data_dir()).await?;
}
@ -235,10 +172,13 @@ impl Profile {
util::clean_up_opts(self.data_dir().join("option"))?;
let hash_check = Self::hash_check(&hash_path, &pkg_hash).await? || refresh;
prepare_packages(&self.meta, &self.data.mods, hash_check).await
.map_err(|e| anyhow!("package configuration failed:\n{:?}", e))?;
let mut ini = self.data.sgt.line_up(&self.meta, self.meta.game).await
.map_err(|e| anyhow!("segatools configuration failed:\n{:?}", e))?;
self.data.network.line_up(&mut ini)?;
if let Some(display) = &self.data.display {
@ -260,10 +200,18 @@ impl Profile {
mu3ini.line_up(&self.data.sgt.target.parent().unwrap())?;
}
if let Some(patches) = &self.data.patches {
futures::try_join!(
patches.render_to_file("amdaemon.exe", patch_files, self.data_dir().join("patch-amd.mph")),
patches.render_to_file("chusanApp.exe", patch_files, self.data_dir().join("patch-game.mph"))
)?;
}
Ok(())
}
pub async fn start(&self, app: AppHandle) -> Result<()> {
pub async fn start(&self, payload: StartPayload) -> Result<()> {
let ini_path = self.data_dir().join("segatools.ini");
log::debug!("With path {:?}", ini_path);
@ -294,10 +242,23 @@ impl Profile {
&ini_path,
)
.current_dir(&exe_dir)
.arg("/C")
.raw_arg("/C")
.arg(&sgt_dir.join(self.meta.game.inject_amd()))
.args(["-d", "-k"])
.arg(sgt_dir.join(self.meta.game.hook_amd()))
.raw_arg("-d")
.raw_arg("-k")
.arg(sgt_dir.join(self.meta.game.hook_amd()));
// for dll in payload.amd_dlls {
// amd_builder.arg("-k");
// amd_builder.arg(dll);
// }
// if self.meta.game.has_module(ProfileModule::Mempatcher) {
// amd_builder.arg("--mempatch");
// amd_builder.arg(self.data_dir().join("patch-amd.mph"));
// }
amd_builder
.arg("amdaemon.exe")
.args(self.meta.game.amd_args());
@ -317,9 +278,16 @@ impl Profile {
self.config_dir().join("saekawa.toml"),
)
.current_dir(&exe_dir)
.args(["-d", "-k"])
.arg(sgt_dir.join(self.meta.game.hook_exe()))
.arg(self.meta.game.exe());
.raw_arg("-d")
.raw_arg("-k")
.arg(sgt_dir.join(self.meta.game.hook_exe()));
for dll in payload.game_dlls {
game_builder.raw_arg("-k");
game_builder.arg(dll);
}
game_builder.arg(self.meta.game.exe());
if self.meta.game.has_module(ProfileModule::BepInEx) {
if let Some(display) = &self.data.display {
@ -339,6 +307,11 @@ impl Profile {
}
}
if self.meta.game.has_module(ProfileModule::Mempatcher) {
game_builder.arg("--mempatch");
game_builder.arg(self.data_dir().join("patch-game.mph"));
}
#[cfg(target_os = "linux")]
{
amd_builder.env("WINEPREFIX", &self.wine.prefix);
@ -383,7 +356,7 @@ impl Profile {
(game.wait().await.expect("game failed to run"), "game")
});
if let Err(e) = app.emit("launch-start", "") {
if let Err(e) = payload.app.emit("launch-start", "") {
log::warn!("Unable to emit launch-start: {}", e);
}
@ -401,7 +374,7 @@ impl Profile {
log::debug!("Fin");
if let Err(e) = app.emit("launch-end", "") {
if let Err(e) = payload.app.emit("launch-end", "") {
log::warn!("Unable to emit launch-end: {}", e);
}