feat: basic card setup

This commit is contained in:
2025-02-25 19:27:37 +00:00
parent 43fd622322
commit 1586f81152
12 changed files with 140 additions and 69 deletions

View File

@ -16,13 +16,13 @@ impl AppData {
.ok_or_else(|| anyhow!("No profile"))?;
if enable {
let pkg = self.pkgs.get(key.clone())?;
let pkg = self.pkgs.get(&key)?;
let loc = pkg.loc
.clone()
.ok_or_else(|| anyhow!("Attempted to enable a non-existent package"))?;
profile.mods.insert(key);
for d in &loc.dependencies {
self.toggle_package(d.clone(), true)?;
_ = self.toggle_package(d.clone(), true);
}
} else {
profile.mods.remove(&key);

View File

@ -55,7 +55,7 @@ pub async fn get_package(state: State<'_, tokio::sync::Mutex<AppData>>, key: Pkg
log::debug!("invoke: get_package({})", key);
let appd = state.lock().await;
appd.pkgs.get(key)
appd.pkgs.get(&key)
.map_err(|e| e.to_string())
.cloned()
}

View File

@ -93,16 +93,6 @@ pub async fn run(_args: Vec<String>) {
});
}));
app.listen("install-end", closure!(clone apph, |ev| {
let payload = serde_json::from_str::<pkg_store::Payload>(&ev.payload());
let apph = apph.clone();
tauri::async_runtime::spawn(async move {
let mutex = apph.state::<Mutex<AppData>>();
let mut appd = mutex.lock().await;
_ = appd.toggle_package(payload.unwrap().pkg, true);
});
}));
Ok(())
})
.invoke_handler(tauri::generate_handler![

View File

@ -13,7 +13,7 @@ async fn symlink(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Resul
#[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)
//std::os::windows::fs::junction_point(src, dst) // is unstable
junction::create(src, dst)
}
@ -77,6 +77,13 @@ pub async fn line_up(p: &Profile) -> Result<()> {
"targetAssembly",
util::path_to_str(dir_out.join("BepInEx").join("core").join("BepInEx.Preloader.dll"))?
);
if prepare_aime(p).await.unwrap_or(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());
@ -87,3 +94,15 @@ pub async fn line_up(p: &Profile) -> Result<()> {
Ok(())
}
// Todo multiple codes
async fn prepare_aime(p: &Profile) -> Result<bool> {
if p.get_bool("aime", true) {
if let Some(code) = p.cfg.get("aime-code") {
let code = code.as_str().expect("Invalid config");
fs::write(util::profile_dir(&p).join("aime.txt"), code).await?;
return Ok(true);
}
}
Ok(false)
}

View File

@ -24,10 +24,11 @@ pub struct Package {
pub rmt: Option<Remote>
}
#[derive(Clone, Default, Serialize, Deserialize)]
#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
pub enum Kind {
Unchecked,
#[default] Mod,
UnsupportedMod
Unsupported
}
#[derive(Clone, Default, Serialize, Deserialize)]

View File

@ -37,8 +37,8 @@ impl PackageStore {
}
}
pub fn get(&self, key: PkgKey) -> Result<&Package> {
self.store.get(&key)
pub fn get(&self, key: &PkgKey) -> Result<&Package> {
self.store.get(key)
.ok_or_else(|| anyhow!("Invalid package key"))
}
@ -49,7 +49,7 @@ impl PackageStore {
pub async fn reload_package(&mut self, key: PkgKey) {
let dir = util::pkg_dir().join(&key.0);
if let Ok(pkg) = Package::from_dir(dir).await {
self.update_package(key, pkg);
self.update_nonremote(key, pkg);
} else {
log::error!("couldn't reload {}", key);
}
@ -68,7 +68,7 @@ impl PackageStore {
while let Some(res) = futures.join_next().await {
if let Ok(Ok(pkg)) = res {
self.update_package(pkg.key(), pkg);
self.update_nonremote(pkg.key(), pkg);
}
}
@ -203,10 +203,11 @@ impl PackageStore {
}
}
fn update_package(&mut self, key: PkgKey, mut new: Package) {
if let Some(old) = self.store.get(&key) {
new.rmt = old.rmt.clone();
fn update_nonremote(&mut self, key: PkgKey, mut new: Package) {
if let Some(old) = self.store.remove(&key) {
new.rmt = old.rmt;
}
self.store.insert(key, new);
}

View File

@ -14,6 +14,8 @@ pub struct Profile {
pub mods: HashSet<PkgKey>,
pub wine_runtime: Option<PathBuf>,
pub wine_prefix: Option<PathBuf>,
// cfg is temporarily just a map to make iteration easier
// eventually it should become strict
pub cfg: HashMap<String, serde_json::Value>
}
@ -62,4 +64,23 @@ impl Profile {
fs::write(&path, s).await.unwrap();
log::info!("Written to {}", path.to_string_lossy());
}
pub fn get_bool(&self, key: &str, default: bool) -> bool {
self.cfg.get(key)
.and_then(|c| c.as_bool())
.unwrap_or(default)
}
pub fn get_int(&self, key: &str, default: i64) -> i64 {
self.cfg.get(key)
.and_then(|c| c.as_i64())
.unwrap_or(default)
}
pub fn get_str(&self, key: &str, default: &str) -> String {
self.cfg.get(key)
.and_then(|c| c.as_str())
.unwrap_or(default)
.to_owned()
}
}

View File

@ -44,41 +44,62 @@ pub fn start(p: &Profile, app: AppHandle) -> Result<()> {
log::info!("Launching amdaemon");
let mut amd_builder = Command::new("cmd.exe");
let mut game_builder = Command::new(p.exe_dir.join("inject.exe"));
let display_mode = p.get_str("display-mode", "borderless");
amd_builder.env(
"SEGATOOLS_CONFIG_PATH",
&ini_path,
)
.creation_flags(create_no_window)
.current_dir(&p.exe_dir)
.args(["/C", &util::path_to_str(p.exe_dir.join( "inject.exe"))?, "-d", "-k", "mu3hook.dll", "amdaemon.exe", "-f", "-c", "config_common.json", "config_server.json", "config_client.json"])
// Obviously this is a meme
// Output will be handled properly at a later time
.stdout(Stdio::null())
.stderr(Stdio::null());
if let Some(v) = p.cfg.get("intel") {
if v == true {
amd_builder.env("OPENSSL_ia32cap", ":~0x20000000");
}
}
let mut amd = amd_builder.spawn()?;
log::info!("Launching mu3");
let mut game = Command::new(p.exe_dir.join( "inject.exe"))
.args([
"/C",
&util::path_to_str(p.exe_dir.join("inject.exe"))?, "-d", "-k", "mu3hook.dll",
"amdaemon.exe", "-f", "-c", "config_common.json", "config_server.json", "config_client.json"
]);
game_builder
.env(
"SEGATOOLS_CONFIG_PATH",
ini_path,
)
.creation_flags(create_no_window)
.current_dir(&p.exe_dir)
.args(["-d", "-k", "mu3hook.dll", "mu3.exe", "-monitor 1", "-screen-fullscreen", "0", "-popupwindow", "-screen-width", "1080", "-screen-height", "1920"])
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()?;
.args([
"-d", "-k", "mu3hook.dll",
"mu3.exe", "-monitor 1",
"-screen-width", &p.get_int("rez-w", 1080).to_string(),
"-screen-height", &p.get_int("rez-h", 1920).to_string(),
"-screen-fullscreen", if display_mode == "fullscreen" { "1" } else { "0" }
]);
if display_mode == "borderless" {
game_builder.arg("-popupwindow");
}
if !cfg!(debug_assertions) {
amd_builder
.creation_flags(create_no_window)
// Obviously, this is a meme
// Output will be handled properly at a later time
.stdout(Stdio::null())
.stderr(Stdio::null());
game_builder
.creation_flags(create_no_window)
.stdout(Stdio::null())
.stderr(Stdio::null());
}
if p.get_bool("intel", false) == true {
amd_builder.env("OPENSSL_ia32cap", ":~0x20000000");
}
let mut amd = amd_builder.spawn()?;
let mut game = game_builder.spawn()?;
tauri::async_runtime::spawn(async move {
let mut set = JoinSet::new();
set.spawn(async move {
amd.wait().await.expect("amdaemon failed to run")
});
@ -91,8 +112,8 @@ pub fn start(p: &Profile, app: AppHandle) -> Result<()> {
log::info!("One of the processes died with return code {}", res);
_ = Command::new("taskkill.exe").arg("/f").arg("/im").arg("amdaemon.exe").output().await;
_ = Command::new("taskkill.exe").arg("/f").arg("/im").arg("mu3.exe").output().await;
_ = Command::new("taskkill.exe").arg("/f").arg("/im").arg("amdaemon.exe").creation_flags(create_no_window).output().await;
_ = Command::new("taskkill.exe").arg("/f").arg("/im").arg("mu3.exe").creation_flags(create_no_window).output().await;
set.join_next().await.expect("No spawn").expect("No result");
@ -102,5 +123,4 @@ pub fn start(p: &Profile, app: AppHandle) -> Result<()> {
});
Ok(())
//Ok((amd, game))
}