forked from akanyan/STARTLINER
feat: audio mode
This commit is contained in:
@ -1,6 +1,9 @@
|
|||||||
|
use enumflags2::make_bitflags;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::pkg::PkgKey;
|
use crate::pkg::PkgKey;
|
||||||
|
|
||||||
|
use super::profile::ProfileModule;
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Copy)]
|
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Copy)]
|
||||||
pub enum Game {
|
pub enum Game {
|
||||||
#[serde(rename = "ongeki")]
|
#[serde(rename = "ongeki")]
|
||||||
@ -59,6 +62,13 @@ impl Game {
|
|||||||
Game::Chunithm => vec!["-c", "config_common.json", "config_server.json", "config_client.json", "config_cvt.json", "config_sp.json", "config_hook.json"]
|
Game::Chunithm => vec!["-c", "config_common.json", "config_server.json", "config_client.json", "config_cvt.json", "config_sp.json", "config_hook.json"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_module(&self, module: ProfileModule) -> bool {
|
||||||
|
match self {
|
||||||
|
Game::Ongeki => make_bitflags!(ProfileModule::{Segatools | Display | Network | BepInEx | Mu3Ini}),
|
||||||
|
Game::Chunithm => make_bitflags!(ProfileModule::{Segatools | Network}),
|
||||||
|
}.contains(module)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Game {
|
impl std::fmt::Display for Game {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::pkg::PkgKey;
|
use crate::pkg::PkgKey;
|
||||||
|
use enumflags2::bitflags;
|
||||||
|
|
||||||
use super::misc::Game;
|
use super::misc::Game;
|
||||||
|
|
||||||
@ -136,3 +137,30 @@ impl Default for Wine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone, PartialEq, Debug, Copy)]
|
||||||
|
pub enum Mu3Audio {
|
||||||
|
Shared,
|
||||||
|
Excl6Ch,
|
||||||
|
Excl2Ch,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||||
|
pub struct Mu3Ini {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub audio: Option<Mu3Audio>,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub blacklist: Option<(i32, i32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[bitflags]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum ProfileModule {
|
||||||
|
Segatools,
|
||||||
|
Network,
|
||||||
|
Display,
|
||||||
|
BepInEx,
|
||||||
|
Mu3Ini
|
||||||
|
}
|
@ -2,6 +2,7 @@ pub mod package;
|
|||||||
pub mod segatools;
|
pub mod segatools;
|
||||||
pub mod network;
|
pub mod network;
|
||||||
pub mod bepinex;
|
pub mod bepinex;
|
||||||
|
pub mod mu3ini;
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub mod display_windows;
|
pub mod display_windows;
|
30
rust/src/modules/mu3ini.rs
Normal file
30
rust/src/modules/mu3ini.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
use anyhow::Result;
|
||||||
|
use ini::Ini;
|
||||||
|
use crate::model::profile::{Mu3Audio, Mu3Ini};
|
||||||
|
|
||||||
|
impl Mu3Ini {
|
||||||
|
pub fn line_up(&self, game_path: impl AsRef<Path>) -> Result<()> {
|
||||||
|
let file = game_path.as_ref().join("mu3.ini");
|
||||||
|
|
||||||
|
if !file.exists() {
|
||||||
|
std::fs::write(&file, "")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ini = Ini::load_from_file(&file)?;
|
||||||
|
|
||||||
|
if let Some(audio) = self.audio {
|
||||||
|
let value = match audio {
|
||||||
|
Mu3Audio::Shared => "0",
|
||||||
|
Mu3Audio::Excl6Ch => "1",
|
||||||
|
Mu3Audio::Excl2Ch => "2",
|
||||||
|
};
|
||||||
|
|
||||||
|
ini.with_section(Some("Sound")).set("WasapiExclusive", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ini.write_to_file(file)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tauri::AppHandle;
|
use tauri::AppHandle;
|
||||||
use std::{collections::BTreeSet, path::{Path, PathBuf}};
|
use std::{collections::BTreeSet, path::{Path, PathBuf}};
|
||||||
use crate::{model::{misc::Game, profile::Aime}, modules::package::prepare_packages, pkg::PkgKey, pkg_store::PackageStore, util};
|
use crate::{model::{misc::Game, profile::{Aime, Mu3Ini, ProfileModule}}, modules::package::prepare_packages, pkg::PkgKey, pkg_store::PackageStore, util};
|
||||||
use tauri::Emitter;
|
use tauri::Emitter;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use crate::model::profile::BepInEx;
|
use crate::model::profile::BepInEx;
|
||||||
@ -42,14 +42,19 @@ pub struct Profile {
|
|||||||
pub struct ProfileData {
|
pub struct ProfileData {
|
||||||
pub mods: BTreeSet<PkgKey>,
|
pub mods: BTreeSet<PkgKey>,
|
||||||
pub sgt: Segatools,
|
pub sgt: Segatools,
|
||||||
pub display: Option<Display>,
|
|
||||||
pub network: Network,
|
pub network: Network,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub display: Option<Display>,
|
||||||
|
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub bepinex: Option<BepInEx>,
|
pub bepinex: Option<BepInEx>,
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
pub wine: crate::model::profile::Wine,
|
pub wine: crate::model::profile::Wine,
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub mu3_ini: Option<Mu3Ini>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Profile {
|
impl Profile {
|
||||||
@ -68,6 +73,7 @@ impl Profile {
|
|||||||
bepinex: if meta.game == Game::Ongeki { Some(BepInEx::default()) } else { None },
|
bepinex: if meta.game == Game::Ongeki { Some(BepInEx::default()) } else { None },
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
wine: crate::model::profile::Wine::default(),
|
wine: crate::model::profile::Wine::default(),
|
||||||
|
mu3_ini: if meta.game == Game::Ongeki { Some(Mu3Ini { audio: None, blacklist: None }) } else { None },
|
||||||
},
|
},
|
||||||
meta: meta.clone()
|
meta: meta.clone()
|
||||||
};
|
};
|
||||||
@ -138,18 +144,25 @@ impl Profile {
|
|||||||
self.data.sgt.fix(store);
|
self.data.sgt.fix(store);
|
||||||
}
|
}
|
||||||
pub fn sync(&mut self, source: ProfileData) {
|
pub fn sync(&mut self, source: ProfileData) {
|
||||||
if self.data.bepinex.is_some() {
|
if self.meta.game.has_module(ProfileModule::BepInEx) && source.bepinex.is_some() {
|
||||||
self.data.bepinex = source.bepinex;
|
self.data.bepinex = source.bepinex;
|
||||||
}
|
}
|
||||||
if self.data.display.is_some() {
|
|
||||||
|
if self.meta.game.has_module(ProfileModule::Display) && source.display.is_some() {
|
||||||
self.data.display = source.display;
|
self.data.display = source.display;
|
||||||
}
|
}
|
||||||
// if self.data.network.is_some() {
|
|
||||||
|
if self.meta.game.has_module(ProfileModule::Network) {
|
||||||
self.data.network = source.network;
|
self.data.network = source.network;
|
||||||
// }
|
}
|
||||||
// if self.data.sgt.is_some() {
|
|
||||||
|
if self.meta.game.has_module(ProfileModule::Segatools) {
|
||||||
self.data.sgt = source.sgt;
|
self.data.sgt = source.sgt;
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
if self.meta.game.has_module(ProfileModule::Mu3Ini) && source.mu3_ini.is_some() {
|
||||||
|
self.data.mu3_ini = source.mu3_ini;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub async fn line_up(&self, pkg_hash: String, refresh: bool, _app: AppHandle) -> Result<()> {
|
pub async fn line_up(&self, pkg_hash: String, refresh: bool, _app: AppHandle) -> Result<()> {
|
||||||
let info = match &self.data.display {
|
let info = match &self.data.display {
|
||||||
@ -194,6 +207,10 @@ impl Profile {
|
|||||||
bepinex.line_up(&self.meta)?;
|
bepinex.line_up(&self.meta)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(mu3ini) = &self.data.mu3_ini {
|
||||||
|
mu3ini.line_up(&self.data.sgt.target.parent().unwrap())?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import SelectButton from 'primevue/selectbutton';
|
||||||
import ToggleSwitch from 'primevue/toggleswitch';
|
import ToggleSwitch from 'primevue/toggleswitch';
|
||||||
import FileEditor from './FileEditor.vue';
|
import FileEditor from './FileEditor.vue';
|
||||||
import OptionCategory from './OptionCategory.vue';
|
import OptionCategory from './OptionCategory.vue';
|
||||||
@ -12,6 +14,54 @@ import { usePrfStore } from '../stores';
|
|||||||
|
|
||||||
const prf = usePrfStore();
|
const prf = usePrfStore();
|
||||||
|
|
||||||
|
const audioModel = computed({
|
||||||
|
get() {
|
||||||
|
return prf.current?.data.mu3_ini?.audio ?? null;
|
||||||
|
},
|
||||||
|
set(value: 'Shared' | 'Excl6Ch' | 'Excl2Ch') {
|
||||||
|
if (prf.current!.data.mu3_ini === undefined) {
|
||||||
|
prf.current!.data.mu3_ini = {};
|
||||||
|
}
|
||||||
|
prf.current!.data.mu3_ini!.audio = value;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// const blacklistMinModel = computed({
|
||||||
|
// get() {
|
||||||
|
// if (prf.current?.data.mu3_ini?.blacklist === undefined) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// return prf.current?.data.mu3_ini?.blacklist[0];
|
||||||
|
// },
|
||||||
|
// set(value: number) {
|
||||||
|
// if (prf.current!.data.mu3_ini === undefined) {
|
||||||
|
// prf.current!.data.mu3_ini = {};
|
||||||
|
// }
|
||||||
|
// prf.current!.data.mu3_ini!.blacklist = [
|
||||||
|
// value,
|
||||||
|
// prf.current!.data.mu3_ini!.blacklist?.[1] ?? 19999,
|
||||||
|
// ];
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const blacklistMaxModel = computed({
|
||||||
|
// get() {
|
||||||
|
// if (prf.current?.data.mu3_ini?.blacklist === undefined) {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
// return prf.current?.data.mu3_ini?.blacklist[1];
|
||||||
|
// },
|
||||||
|
// set(value: number) {
|
||||||
|
// if (prf.current!.data.mu3_ini === undefined) {
|
||||||
|
// prf.current!.data.mu3_ini = {};
|
||||||
|
// }
|
||||||
|
// prf.current!.data.mu3_ini!.blacklist = [
|
||||||
|
// prf.current!.data.mu3_ini!.blacklist?.[0] ?? 10000,
|
||||||
|
// value,
|
||||||
|
// ];
|
||||||
|
// },
|
||||||
|
// });
|
||||||
|
|
||||||
prf.reload();
|
prf.reload();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -36,6 +86,47 @@ prf.reload();
|
|||||||
<!-- @vue-expect-error -->
|
<!-- @vue-expect-error -->
|
||||||
<ToggleSwitch v-model="prf.current!.data.bepinex.console" />
|
<ToggleSwitch v-model="prf.current!.data.bepinex.console" />
|
||||||
</OptionRow>
|
</OptionRow>
|
||||||
|
|
||||||
|
<OptionRow
|
||||||
|
title="Audio mode"
|
||||||
|
tooltip="Exclusive 2-channel mode requires a patch"
|
||||||
|
>
|
||||||
|
<SelectButton
|
||||||
|
v-model="audioModel"
|
||||||
|
:options="[
|
||||||
|
{ title: 'Shared', value: 'Shared' },
|
||||||
|
{ title: 'Exclusive 6-channel', value: 'Excl6Ch' },
|
||||||
|
{ title: 'Exclusive 2-channel', value: 'Excl2Ch' },
|
||||||
|
]"
|
||||||
|
:allow-empty="true"
|
||||||
|
option-label="title"
|
||||||
|
option-value="value"
|
||||||
|
/></OptionRow>
|
||||||
|
|
||||||
|
<!-- <OptionRow
|
||||||
|
class="number-input"
|
||||||
|
title="Song ID Blacklist"
|
||||||
|
tooltip="Requires a patch"
|
||||||
|
><InputNumber
|
||||||
|
class="shrink"
|
||||||
|
size="small"
|
||||||
|
:min="10000"
|
||||||
|
:max="99999"
|
||||||
|
placeholder="10000"
|
||||||
|
:use-grouping="false"
|
||||||
|
:allow-empty="false"
|
||||||
|
v-model="blacklistMinModel" />
|
||||||
|
x
|
||||||
|
<InputNumber
|
||||||
|
class="shrink"
|
||||||
|
size="small"
|
||||||
|
:min="10000"
|
||||||
|
:max="99999"
|
||||||
|
placeholder="19999"
|
||||||
|
:use-grouping="false"
|
||||||
|
:allow-empty="false"
|
||||||
|
v-model="blacklistMaxModel"
|
||||||
|
/></OptionRow> -->
|
||||||
</OptionCategory>
|
</OptionCategory>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ const prf = usePrfStore();
|
|||||||
icon="pi pi-plus"
|
icon="pi pi-plus"
|
||||||
class="chunithm-button profile-button"
|
class="chunithm-button profile-button"
|
||||||
@click="() => prf.create('chunithm')"
|
@click="() => prf.create('chunithm')"
|
||||||
|
v-tooltip="'!!! Experimental !!!'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-12 flex flex-col flex-wrap align-middle gap-4">
|
<div class="mt-12 flex flex-col flex-wrap align-middle gap-4">
|
||||||
|
@ -31,6 +31,15 @@ const aimeCodeModel = computed({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const aimeCodePaste = (ev: ClipboardEvent) => {
|
||||||
|
aimeCodeModel.value =
|
||||||
|
ev.clipboardData
|
||||||
|
?.getData('text/plain')
|
||||||
|
.split('')
|
||||||
|
.filter((c) => c >= '0' && c <= '9')
|
||||||
|
.join('') ?? '';
|
||||||
|
};
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const aime_path = await path.join(await prf.configDir, 'aime.txt');
|
const aime_path = await path.join(await prf.configDir, 'aime.txt');
|
||||||
aimeCode.value = await readTextFile(aime_path).catch(() => '');
|
aimeCode.value = await readTextFile(aime_path).catch(() => '');
|
||||||
@ -67,6 +76,7 @@ const aimeCodeModel = computed({
|
|||||||
:maxlength="20"
|
:maxlength="20"
|
||||||
placeholder="00000000000000000000"
|
placeholder="00000000000000000000"
|
||||||
v-model="aimeCodeModel"
|
v-model="aimeCodeModel"
|
||||||
|
@paste="aimeCodePaste"
|
||||||
/>
|
/>
|
||||||
</OptionRow>
|
</OptionRow>
|
||||||
<div v-if="prf.current!.data.sgt.aime?.hasOwnProperty('AMNet')">
|
<div v-if="prf.current!.data.sgt.aime?.hasOwnProperty('AMNet')">
|
||||||
|
@ -52,6 +52,7 @@ export interface ProfileData {
|
|||||||
display: DisplayConfig;
|
display: DisplayConfig;
|
||||||
network: NetworkConfig;
|
network: NetworkConfig;
|
||||||
bepinex: BepInExConfig;
|
bepinex: BepInExConfig;
|
||||||
|
mu3_ini: Mu3IniConfig | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SegatoolsConfig {
|
export interface SegatoolsConfig {
|
||||||
@ -88,10 +89,16 @@ export interface NetworkConfig {
|
|||||||
subnet: string;
|
subnet: string;
|
||||||
suffix: number | null;
|
suffix: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BepInExConfig {
|
export interface BepInExConfig {
|
||||||
console: boolean;
|
console: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Mu3IniConfig {
|
||||||
|
audio?: 'Shared' | 'Excl6Ch' | 'Excl2Ch';
|
||||||
|
// blacklist?: [number, number];
|
||||||
|
}
|
||||||
|
|
||||||
export interface Profile {
|
export interface Profile {
|
||||||
meta: ProfileMeta;
|
meta: ProfileMeta;
|
||||||
data: ProfileData;
|
data: ProfileData;
|
||||||
|
Reference in New Issue
Block a user