feat: initial support for segatools pkgs

This commit is contained in:
2025-03-15 00:08:33 +01:00
parent b525e74467
commit caa20a3aa0
20 changed files with 246 additions and 112 deletions

View File

@ -45,11 +45,15 @@ pub async fn kill() -> Result<(), String> {
}
#[tauri::command]
pub async fn install_package(state: State<'_, tokio::sync::Mutex<AppData>>, key: PkgKey) -> Result<InstallResult, String> {
pub async fn install_package(
state: State<'_, tokio::sync::Mutex<AppData>>,
key: PkgKey,
force: bool
) -> Result<InstallResult, String> {
log::debug!("invoke: install_package({})", key);
let mut appd = state.lock().await;
appd.pkgs.install_package(&key, true, true)
appd.pkgs.install_package(&key, force, true)
.await
.map_err(|e| e.to_string())
}

View File

@ -1,9 +1,12 @@
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::pkg::PkgKey;
#[derive(Deserialize, Serialize, Clone)]
pub struct Segatools {
pub target: PathBuf,
pub hook: Option<PkgKey>,
pub io: Option<PkgKey>,
pub amfs: PathBuf,
pub option: PathBuf,
pub appdata: PathBuf,
@ -15,6 +18,8 @@ impl Default for Segatools {
fn default() -> Self {
Segatools {
target: PathBuf::default(),
hook: Some(PkgKey("segatools-mu3hook".to_owned())),
io: None,
amfs: PathBuf::default(),
option: PathBuf::default(),
appdata: PathBuf::from("appdata"),

View File

@ -1,14 +1,16 @@
use std::collections::BTreeSet;
use std::collections::{BTreeMap, BTreeSet};
use serde::Deserialize;
use crate::pkg::PkgKeyVersion;
// manifest.json
#[derive(Deserialize)]
#[allow(dead_code)]
pub struct PackageManifest {
pub name: String,
pub version_number: String,
pub description: String,
pub dependencies: BTreeSet<PkgKeyVersion>
}
pub dependencies: BTreeSet<PkgKeyVersion>,
#[serde(default)]
pub installers: Vec<BTreeMap<String, serde_json::Value>>
}

View File

@ -1,9 +1,5 @@
pub fn segatools_base() -> String {
"; mu3io is TBD
[mu3io]
path=
[vfd]
"[vfd]
; Enable VFD emulation. Disable to use a real VFD
; GP1232A02A FUTABA assembly.
enable=1

View File

@ -57,6 +57,14 @@ impl Segatools {
.set("enable", "0");
}
if let Some(io) = &self.io {
ini_out.with_section(Some("mu3io"))
.set("path", util::pkg_dir().join(io.to_string()).join("mu3io.dll").stringify()?);
} else {
ini_out.with_section(Some("mu3io"))
.set("path", "");
}
log::debug!("option dir: {:?} -> {:?}", opt_dir_in, opt_dir_out);
if !opt_dir_out.exists() {

View File

@ -3,7 +3,7 @@ use derive_more::Display;
use serde::{Deserialize, Serialize};
use std::{collections::BTreeSet, path::{Path, PathBuf}};
use tokio::fs;
use crate::{model::{local, rainy}, util};
use crate::{model::{local::{self, PackageManifest}, rainy}, util};
// {namespace}-{name}
#[derive(Eq, Hash, PartialEq, PartialOrd, Ord, Clone, Serialize, Deserialize, Display)]
@ -27,8 +27,10 @@ pub struct Package {
#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
pub enum Kind {
Unchecked,
Unsupported,
#[default] Mod,
Unsupported
Hook,
IO,
}
#[derive(Clone, Default, Serialize, Deserialize)]
@ -84,6 +86,7 @@ impl Package {
.unwrap()
.to_owned();
let kind = Self::parse_kind(&mft);
let dependencies = Self::sanitize_deps(mft.dependencies);
Ok(Package {
@ -94,7 +97,7 @@ impl Package {
loc: Some(Local {
version: mft.version_number,
path: dir.to_owned(),
kind: Kind::Mod,
kind,
dependencies
}),
rmt: None
@ -166,4 +169,25 @@ impl Package {
}
res
}
fn parse_kind(mft: &PackageManifest) -> Kind {
if mft.installers.len() == 0 {
return Kind::Mod;//Unchecked
} else if mft.installers.len() == 1 {
if let Some(serde_json::Value::String(id)) = &mft.installers[0].get("identifier") {
if id == "rainycolor" {
return Kind::Mod
} else if id == "segatools" {
if let Some(serde_json::Value::String(module)) = mft.installers[0].get("module") {
if module.ends_with("hook") {
return Kind::Hook;
} else if module.ends_with("io") {
return Kind::IO;
}
}
}
}
}
return Kind::Unsupported
}
}

View File

@ -120,13 +120,14 @@ impl PackageStore {
}
pub async fn install_package(&mut self, key: &PkgKey, force: bool, install_deps: bool) -> Result<InstallResult> {
log::debug!("Installing {}", key);
log::info!("installation request: {}/{}/{}", key, force, install_deps);
let pkg = self.store.get(key)
.ok_or_else(|| anyhow!("Attempted to install a nonexistent pkg"))?
.clone();
if pkg.loc.is_some() && !force {
log::debug!("installation skipped");
return Ok(InstallResult::Ready);
}
@ -152,7 +153,7 @@ impl PackageStore {
if !zip_path.exists() {
self.dlh.download_zip(&zip_path, &pkg)?;
log::debug!("Deferring {}", key);
log::debug!("deferring {}", key);
return Ok(InstallResult::Deferred);
}
@ -170,13 +171,13 @@ impl PackageStore {
pkg: key.to_owned()
})?;
log::info!("Installed {}", key);
log::info!("installed {}", key);
Ok(InstallResult::Ready)
}
pub async fn delete_package(&mut self, key: &PkgKey, force: bool) -> Result<()> {
log::debug!("Will delete {} {}", key, force);
log::debug!("will delete {} {}", key, force);
let pkg = self.store.get_mut(key)
.ok_or_else(|| anyhow!("Attempted to delete a nonexistent pkg"))?;
@ -191,7 +192,7 @@ impl PackageStore {
self.app.emit("install-end", Payload {
pkg: key.to_owned()
})?;
log::info!("Deleted {}", key);
log::info!("deleted {}", key);
}
rv
} else {
@ -216,7 +217,7 @@ impl PackageStore {
if path.exists() {
tokio::fs::remove_dir_all(path)
.await
.map_err(|e| anyhow!("Could not delete /{}: {}", name, e))?;
.map_err(|e| anyhow!("could not delete {}: {}", name, e))?;
}
Ok(())
@ -237,6 +238,7 @@ impl PackageStore {
// todo case sensitivity for linux
Self::clean_up_dir(&path, "app").await?;
Self::clean_up_dir(&path, "option").await?;
Self::clean_up_dir(&path, "segatools").await?;
Self::clean_up_file(&path, "icon.png", true).await?;
Self::clean_up_file(&path, "manifest.json", true).await?;
Self::clean_up_file(&path, "README.md", true).await?;

View File

@ -3,7 +3,7 @@ use ongeki::OngekiProfile;
use serde::{Deserialize, Serialize};
use tauri::AppHandle;
use std::{collections::BTreeSet, path::{Path, PathBuf}};
use crate::{model::{config::Display, misc::Game}, modules::package::prepare_packages, pkg::PkgKey, util};
use crate::{model::misc::Game, modules::package::prepare_packages, pkg::PkgKey, util};
pub mod ongeki;
@ -72,14 +72,15 @@ impl AnyProfile {
}
pub async fn line_up(&self, pkg_hash: String, _app: AppHandle) -> Result<()> {
match self {
Self::OngekiProfile(p) => {
Self::OngekiProfile(_p) => {
#[cfg(target_os = "windows")]
let info = p.display.line_up()?;
let info = _p.display.line_up()?;
let res = self.line_up_the_rest(pkg_hash).await;
#[cfg(target_os = "windows")]
if let Some(info) = info {
use crate::model::config::Display;
if res.is_ok() {
Display::wait_for_exit(_app, info);
} else {

View File

@ -88,10 +88,13 @@ impl Profile for OngekiProfile {
let target_path = PathBuf::from(&self.sgt.target);
let exe_dir = target_path.parent().ok_or_else(|| anyhow!("Invalid target path"))?;
let sgt_dir = util::pkg_dir()
.join(self.sgt.hook.as_ref().ok_or_else(|| anyhow!("No hook"))?.to_string())
.join("segatools");
#[cfg(target_os = "windows")]
{
game_builder = Command::new(exe_dir.join("inject.exe"));
game_builder = Command::new(sgt_dir.join("inject.exe"));
amd_builder = Command::new("cmd.exe");
}
#[cfg(target_os = "linux")]
@ -99,7 +102,7 @@ impl Profile for OngekiProfile {
game_builder = Command::new(&self.wine.runtime);
amd_builder = Command::new(&self.wine.runtime);
game_builder.arg(exe_dir.join("inject.exe"));
game_builder.arg(sgt_dir.join("inject.exe"));
amd_builder.arg("cmd.exe");
}
@ -109,10 +112,10 @@ impl Profile for OngekiProfile {
)
.current_dir(&exe_dir)
.arg("/C")
.arg(&exe_dir.join("inject.exe"))
.args([
"-d", "-k", "mu3hook.dll",
"amdaemon.exe", "-f", "-c", "config_common.json", "config_server.json", "config_client.json"
.arg(&sgt_dir.join("inject.exe"))
.args(["-d", "-k"])
.arg(sgt_dir.join("mu3hook.dll"))
.args(["amdaemon.exe", "-f", "-c", "config_common.json", "config_server.json", "config_client.json"
]);
game_builder
.env(
@ -124,8 +127,9 @@ impl Profile for OngekiProfile {
self.config_dir().join("inohara.cfg"),
)
.current_dir(&exe_dir)
.args(["-d", "-k"])
.arg(sgt_dir.join("mu3hook.dll"))
.args([
"-d", "-k", "mu3hook.dll",
"mu3.exe", "-monitor 1",
"-screen-width", &self.display.rez.0.to_string(),
"-screen-height", &self.display.rez.1.to_string(),