diff --git a/CHANGELOG.md b/CHANGELOG.md index 162d2b1..9c81697 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 0.8.0 + +- Added support for ChuniIO + +## 0.7.1 + +- Hotfixed amdaemon crashing at launch +- Greyed out packages currently incompatible with STARTLINER + ## 0.7.0 - Hopefully fixed issues with the download button diff --git a/README.md b/README.md index a99b861..972eeb3 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,6 @@ Made with Rust (Tauri) and Vue. Technically multiplatform. Contributions welcome - Display configuration with automatic rollback - Support for multiple configurations pointing at the same data -![Mod list](res/list.png) -![Configuration](res/cfg.png) - ## Usage Download a prebuilt binary from [Releases](https://gitea.tendokyu.moe/akanyan/STARTLINER/releases) or build it yourself: @@ -35,25 +32,15 @@ To create a desktop shortcut: `Copy -> Paste Shortcut -> Properties`, and then a ## Package format -- [Package format requirements](https://rainy.patafour.zip/package/create/docs/) -- A subset of the simple BlueSteel Rainycolor format is currently supported. [Full reference (CW: vore)](https://yozora.bluesteel.737.jp.net/HarmonyPublic/SOS-Kongou/wiki/Create-Module#user-content-rainycolor-simple) - -``` -├───app -│ └───BepInEx -│ └───* -├───option -│ └───Axyz -│ └───* -├───icon.png -├───README.md -└───manifest.json -``` - -More file overrides may be supported in the future. - -Arbitrary scripts are not supported by design and that will probably never change. +Refer to [the wiki](https://gitea.tendokyu.moe/akanyan/STARTLINER/wiki/Package-format). ## See also -- [BlueSteel launcher (CW: vore)](https://yozora.bluesteel.737.jp.net/HarmonyPublic/SOS-Kongou) +[BlueSteel launcher (CW: vore)](https://yozora.bluesteel.737.jp.net/HarmonyPublic/SOS-Kongou) + +## Screenshots + +![Package list](res/list.png) +![Package store](res/store.png) +![Configuration](res/cfg.png) +![Keyboard](res/cfg2.png) diff --git a/res/cfg.png b/res/cfg.png index 93b8e66..b1e371e 100644 Binary files a/res/cfg.png and b/res/cfg.png differ diff --git a/res/cfg2.png b/res/cfg2.png new file mode 100644 index 0000000..1dadb81 Binary files /dev/null and b/res/cfg2.png differ diff --git a/res/list.png b/res/list.png index 15313e7..8a5f0c0 100644 Binary files a/res/list.png and b/res/list.png differ diff --git a/res/store.png b/res/store.png new file mode 100644 index 0000000..80be7f1 Binary files /dev/null and b/res/store.png differ diff --git a/rust/icons/icon.ico b/rust/icons/icon.ico new file mode 100644 index 0000000..9cdec2b Binary files /dev/null and b/rust/icons/icon.ico differ diff --git a/rust/icons/icon.png b/rust/icons/icon.png new file mode 100644 index 0000000..3b5b239 Binary files /dev/null and b/rust/icons/icon.png differ diff --git a/rust/icons/ongeki.ico b/rust/icons/ongeki.ico deleted file mode 100644 index 282cbe0..0000000 Binary files a/rust/icons/ongeki.ico and /dev/null differ diff --git a/rust/icons/slow.ico b/rust/icons/slow.ico deleted file mode 100644 index 90a5442..0000000 Binary files a/rust/icons/slow.ico and /dev/null differ diff --git a/rust/icons/slow.png b/rust/icons/slow.png deleted file mode 100644 index 64b8d7b..0000000 Binary files a/rust/icons/slow.png and /dev/null differ diff --git a/rust/src/model/profile.rs b/rust/src/model/profile.rs index 14e1ab6..2e41250 100644 --- a/rust/src/model/profile.rs +++ b/rust/src/model/profile.rs @@ -13,6 +13,14 @@ pub enum Aime { Other(PkgKey), } +#[derive(Deserialize, Serialize, Clone, Default, PartialEq, Debug)] +#[serde(rename_all = "snake_case")] +pub enum IOSelection { + Hardware, + #[default] SegatoolsBuiltIn, + Custom(PkgKey) +} + #[derive(Deserialize, Serialize, Clone, Debug)] #[serde(default)] pub struct AMNet { @@ -32,7 +40,9 @@ impl Default for AMNet { pub struct Segatools { pub target: PathBuf, pub hook: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub io: Option, + pub io2: IOSelection, pub aime: Aime, pub amfs: PathBuf, pub option: PathBuf, @@ -51,6 +61,7 @@ impl Segatools { Game::Chunithm => Some(PkgKey("segatools-chusanhook".to_owned())) }, io: None, + io2: IOSelection::SegatoolsBuiltIn, amfs: PathBuf::default(), option: PathBuf::default(), appdata: PathBuf::from("appdata"), diff --git a/rust/src/modules/keyboard.rs b/rust/src/modules/keyboard.rs index 4df7ede..fcd0b5b 100644 --- a/rust/src/modules/keyboard.rs +++ b/rust/src/modules/keyboard.rs @@ -95,7 +95,19 @@ impl Keyboard { .set("mouse", if kb.use_mouse { "1" } else { "0" }); } else { ini.with_section(Some("io4")) - .set("enable", "0"); + .set("test", "0") + .set("service", "0") + .set("coin", "0") + .set("left1", "0") + .set("left2", "0") + .set("left3", "0") + .set("right1", "0") + .set("right2", "0") + .set("right3", "0") + .set("leftSide", "0") + .set("rightSide", "0") + .set("leftMenu", "0") + .set("rightMenu", "0"); } } Keyboard::Chunithm(kb) => { @@ -109,14 +121,21 @@ impl Keyboard { ini.with_section(Some("io3")) .set("test", kb.test.to_string()) .set("service", kb.svc.to_string()) - .set("coin", kb.coin.to_string()) - .set("ir", "0"); + .set("coin", kb.coin.to_string()); } else { - ini.with_section(Some("io4")) - .set("enable", "0"); - ini.with_section(Some("slider")) - .set("enable", "0"); + for (i, _) in kb.cell.iter().enumerate() { + ini.with_section(Some("slider")).set(format!("cell{}", i + 1), "0"); + } + for (i, _) in kb.ir.iter().enumerate() { + ini.with_section(Some("ir")).set(format!("ir{}", i + 1), "0"); + } + ini.with_section(Some("io3")) + .set("test", "0") + .set("service", "0") + .set("coin", "0"); } + ini.with_section(Some("io3")) + .set("ir", "0"); } } diff --git a/rust/src/modules/segatools.rs b/rust/src/modules/segatools.rs index ecacd55..0f36613 100644 --- a/rust/src/modules/segatools.rs +++ b/rust/src/modules/segatools.rs @@ -1,7 +1,7 @@ use std::path::{PathBuf, Path}; use anyhow::{anyhow, Result}; use ini::Ini; -use crate::{model::{misc::{ConfigHook, ConfigHookAime, ConfigHookAimeUnit, ConfigHookAuth, Game}, profile::{Aime, Segatools}}, profiles::ProfilePaths, util::{self, PathStr}}; +use crate::{model::{misc::{ConfigHook, ConfigHookAime, ConfigHookAimeUnit, ConfigHookAuth, Game}, profile::{Aime, IOSelection, Segatools}}, profiles::ProfilePaths, util::{self, PathStr}}; use crate::pkg_store::PackageStore; impl Segatools { @@ -21,8 +21,8 @@ impl Segatools { if let Some(key) = &self.hook { remove_if_nonpresent!(self.hook, key, None, store); } - if let Some(key) = &self.io { - remove_if_nonpresent!(self.io, key, None, store); + if let IOSelection::Custom(key) = &self.io2 { + remove_if_nonpresent!(self.io2, key, IOSelection::default(), store); } match &self.aime { Aime::AMNet(key) => remove_if_nonpresent!(self.aime, key, Aime::BuiltIn, store), @@ -141,7 +141,7 @@ impl Segatools { } if game == Game::Ongeki { - if let Some(io) = &self.io { + if let IOSelection::Custom(io) = &self.io2 { ini_out.with_section(Some("mu3io")) .set("path", util::pkg_dir().join(io.to_string()).join("segatools").join("mu3io.dll").stringify()?); } else { @@ -149,6 +149,42 @@ impl Segatools { .set("path", ""); } } + match game { + Game::Ongeki => { + match &self.io2 { + IOSelection::Custom(io) => { + ini_out.with_section(Some("mu3io")) + .set("path", util::pkg_dir().join(io.to_string()).join("segatools").join("mu3io.dll").stringify()?); + } + IOSelection::SegatoolsBuiltIn => { + ini_out.with_section(Some("mu3io")) + .set("path", ""); + } + IOSelection::Hardware => { + ini_out.with_section(Some("io4")) + .set("enable", "0"); + } + } + }, + Game::Chunithm => { + match &self.io2 { + IOSelection::Custom(io) => { + ini_out.with_section(Some("chuniio")) + .set("path32", util::pkg_dir().join(io.to_string()).join("segatools").join("chuniio32.dll").stringify()?) + .set("path64", util::pkg_dir().join(io.to_string()).join("segatools").join("chuniio64.dll").stringify()?); + } + IOSelection::SegatoolsBuiltIn => { + ini_out.with_section(Some("chuniio")) + .set("path32", "") + .set("path64", ""); + } + IOSelection::Hardware => { + ini_out.with_section(Some("io4")) + .set("enable", "0"); + } + } + } + }; log::debug!("option dir: {:?} -> {:?}", opt_dir_in, opt_dir_out); diff --git a/rust/src/pkg.rs b/rust/src/pkg.rs index 1accaae..b99a823 100644 --- a/rust/src/pkg.rs +++ b/rust/src/pkg.rs @@ -239,20 +239,14 @@ impl Package { if id == "rainycolor" { flags |= Feature::Mod; } else if id == "segatools" { - // Multiple features in the same dll (yubideck etc.) should be supported at some point if let Some(serde_json::Value::String(module)) = installer.get("module") { - if module == "mu3hook" { - flags |= Feature::Mu3Hook; - } else if module == "chusanhook" { - flags |= Feature::ChusanHook; - } else if module == "amnet" { - flags |= Feature::AMNet | Feature::Aime; - } else if module == "aimeio" { - flags |= Feature::Aime; - } else if module == "mu3io" { - flags |= Feature::Mu3IO; - } else if module == "chuniio" { - flags |= Feature::ChuniIO; + flags |= Self::parse_segatools_module(&module); + } + if let Some(serde_json::Value::Array(arr)) = installer.get("module") { + for elem in arr { + if let serde_json::Value::String(module) = elem { + flags |= Self::parse_segatools_module(module); + } } } } else if id == "native_mod" { @@ -280,4 +274,16 @@ impl Package { Status::OK(flags, DLLs { game: game_dll, amd: amd_dll }) } } + + fn parse_segatools_module(module: &str) -> BitFlags { + match module { + "mu3hook" => make_bitflags!(Feature::Mu3Hook), + "chusanhook" => make_bitflags!(Feature::ChusanHook), + "amnet" => make_bitflags!(Feature::{AMNet | Aime}), + "aimeio" => make_bitflags!(Feature::Aime), + "mu3io" => make_bitflags!(Feature::Mu3IO), + "chuniio" => make_bitflags!(Feature::ChuniIO), + _ => BitFlags::default() + } + } } \ No newline at end of file diff --git a/rust/src/profiles/mod.rs b/rust/src/profiles/mod.rs index 89028d3..cf3553f 100644 --- a/rust/src/profiles/mod.rs +++ b/rust/src/profiles/mod.rs @@ -1,6 +1,6 @@ pub use types::{Profile, ProfileData, ProfileMeta, ProfilePaths, StartPayload}; use std::{collections::{BTreeMap, BTreeSet}, path::{Path, PathBuf}}; -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 crate::{model::{misc::Game, patch::{PatchFileVec, PatchSelection}, profile::{Aime, ChunithmKeyboard, IOSelection, 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; @@ -59,8 +59,14 @@ impl Profile { log::debug!("{:?}", data); // Backwards compat - if game == Game::Ongeki && data.keyboard.is_none() { - data.keyboard = Some(Keyboard::Ongeki(OngekiKeyboard::default())); + if game == Game::Ongeki { + if data.keyboard.is_none() { + data.keyboard = Some(Keyboard::Ongeki(OngekiKeyboard::default())); + } + if let Some(io) = data.sgt.io { + data.sgt.io2 = IOSelection::Custom(io); + data.sgt.io = None; + } } if game == Game::Chunithm { if data.keyboard.is_none() { @@ -112,7 +118,7 @@ impl Profile { if let Some(hook) = &self.data.sgt.hook { res.push(hook.clone()); } - if let Some(io) = &self.data.sgt.io { + if let IOSelection::Custom(io) = &self.data.sgt.io2 { res.push(io.clone()); } if let Aime::AMNet(aime) = &self.data.sgt.aime { diff --git a/rust/tauri.conf.json b/rust/tauri.conf.json index 3fd5489..180049f 100644 --- a/rust/tauri.conf.json +++ b/rust/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "STARTLINER", - "version": "0.7.0", + "version": "0.8.0", "identifier": "zip.patafour.startliner", "build": { "beforeDevCommand": "bun run dev", @@ -66,7 +66,7 @@ "bundle": { "active": true, "targets": "all", - "icon": ["icons/slow.png", "icons/slow.ico"], + "icon": ["icons/icon.png", "icons/icon.ico"], "createUpdaterArtifacts": true } } diff --git a/src/components/options/Keyboard.vue b/src/components/options/Keyboard.vue index f3848c4..cb0d880 100644 --- a/src/components/options/Keyboard.vue +++ b/src/components/options/Keyboard.vue @@ -11,7 +11,10 @@ const prf = usePrfStore();