feat: initial chunithm support
This commit is contained in:
@ -1,399 +1,30 @@
|
||||
<script setup lang="ts">
|
||||
import { Ref, computed, ref } from 'vue';
|
||||
import InputNumber from 'primevue/inputnumber';
|
||||
import InputText from 'primevue/inputtext';
|
||||
import Select from 'primevue/select';
|
||||
import SelectButton from 'primevue/selectbutton';
|
||||
import ToggleSwitch from 'primevue/toggleswitch';
|
||||
import * as path from '@tauri-apps/api/path';
|
||||
import { readTextFile, writeTextFile } from '@tauri-apps/plugin-fs';
|
||||
import FileEditor from './FileEditor.vue';
|
||||
import FilePicker from './FilePicker.vue';
|
||||
import OptionCategory from './OptionCategory.vue';
|
||||
import OptionRow from './OptionRow.vue';
|
||||
import { invoke } from '../invoke';
|
||||
import { usePkgStore, usePrfStore } from '../stores';
|
||||
import { Feature } from '../types';
|
||||
import { hasFeature, pkgKey } from '../util';
|
||||
import AimeOptions from './options/Aime.vue';
|
||||
import DisplayOptions from './options/Display.vue';
|
||||
import MiscOptions from './options/Misc.vue';
|
||||
import NetworkOptions from './options/Network.vue';
|
||||
import SegatoolsOptions from './options/Segatools.vue';
|
||||
import { usePrfStore } from '../stores';
|
||||
|
||||
const pkgs = usePkgStore();
|
||||
const prf = usePrfStore();
|
||||
|
||||
const aimeCode = ref('');
|
||||
const capabilities: Ref<string[]> = ref([]);
|
||||
|
||||
const displayList: Ref<{ title: string; value: string }[]> = ref([
|
||||
{
|
||||
title: 'Primary',
|
||||
value: 'default',
|
||||
},
|
||||
]);
|
||||
|
||||
const loadDisplays = () => {
|
||||
const newList = [
|
||||
{
|
||||
title: 'Primary',
|
||||
value: 'default',
|
||||
},
|
||||
];
|
||||
invoke('list_platform_capabilities')
|
||||
.then(async (v: unknown) => {
|
||||
let different = false;
|
||||
if (Array.isArray(v)) {
|
||||
capabilities.value.push(...v);
|
||||
}
|
||||
if (capabilities.value.includes('display')) {
|
||||
for (const [devName, devString] of (await invoke(
|
||||
'list_displays'
|
||||
)) as Array<[string, string]>) {
|
||||
newList.push({
|
||||
title: `${devName.replace('\\\\.\\', '')} (${devString})`,
|
||||
value: devName,
|
||||
});
|
||||
if (
|
||||
displayList.value.find(
|
||||
(item) => item.value === devName
|
||||
) === undefined
|
||||
) {
|
||||
different = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (displayList.value.length !== newList.length) {
|
||||
different = true;
|
||||
}
|
||||
if (different) {
|
||||
displayList.value = newList;
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
loadDisplays();
|
||||
prf.reload();
|
||||
|
||||
const aimeCodeModel = computed({
|
||||
get() {
|
||||
return aimeCode.value;
|
||||
},
|
||||
async set(value: string) {
|
||||
aimeCode.value = value;
|
||||
if (value.match(/^[0-9]{20}$/) || value.length === 0) {
|
||||
const aime_path = await path.join(await prf.configDir, 'aime.txt');
|
||||
await writeTextFile(aime_path, aimeCode.value);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const extraDisplayOptionsDisabled = computed(() => {
|
||||
return prf.current?.display.target === 'default';
|
||||
});
|
||||
|
||||
(async () => {
|
||||
const aime_path = await path.join(await prf.configDir, 'aime.txt');
|
||||
aimeCode.value = await readTextFile(aime_path).catch(() => '');
|
||||
})();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<OptionCategory title="General">
|
||||
<OptionRow title="mu3.exe">
|
||||
<FilePicker
|
||||
:directory="false"
|
||||
promptname="mu3.exe"
|
||||
extension="exe"
|
||||
:value="prf.current!.sgt.target"
|
||||
:callback="(value: string) => (prf.current!.sgt.target = value)"
|
||||
></FilePicker>
|
||||
</OptionRow>
|
||||
|
||||
<OptionRow title="amfs">
|
||||
<FilePicker
|
||||
:directory="true"
|
||||
placeholder="amfs"
|
||||
:value="prf.current!.sgt.amfs"
|
||||
:callback="(value: string) => (prf.current!.sgt.amfs = value)"
|
||||
></FilePicker>
|
||||
</OptionRow>
|
||||
<OptionRow title="option">
|
||||
<FilePicker
|
||||
:directory="true"
|
||||
placeholder="option"
|
||||
:value="prf.current!.sgt.option"
|
||||
:callback="(value: string) => (prf.current!.sgt.option = value)"
|
||||
></FilePicker>
|
||||
</OptionRow>
|
||||
<OptionRow title="appdata">
|
||||
<FilePicker
|
||||
:directory="true"
|
||||
:value="prf.current!.sgt.appdata"
|
||||
:callback="
|
||||
(value: string) => (prf.current!.sgt.appdata = value)
|
||||
"
|
||||
></FilePicker>
|
||||
</OptionRow>
|
||||
<OptionRow title="mu3hook">
|
||||
<Select
|
||||
v-model="prf.current!.sgt.hook"
|
||||
:options="
|
||||
pkgs.hooks.map((p) => {
|
||||
return { title: pkgKey(p), value: pkgKey(p) };
|
||||
})
|
||||
"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
></Select>
|
||||
</OptionRow>
|
||||
<OptionRow title="mu3io">
|
||||
<Select
|
||||
v-model="prf.current!.sgt.io"
|
||||
placeholder="segatools built-in"
|
||||
:options="[
|
||||
{ title: 'segatools built-in', value: null },
|
||||
...pkgs.gameIOs.map((p) => {
|
||||
return { title: pkgKey(p), value: pkgKey(p) };
|
||||
}),
|
||||
]"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
></Select>
|
||||
</OptionRow>
|
||||
</OptionCategory>
|
||||
<OptionCategory title="Display">
|
||||
<OptionRow
|
||||
v-if="capabilities.includes('display')"
|
||||
title="Target display"
|
||||
>
|
||||
<Select
|
||||
v-model="prf.current!.display.target"
|
||||
:options="displayList"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
placeholder="(Disconnected)"
|
||||
@show="loadDisplays"
|
||||
></Select>
|
||||
</OptionRow>
|
||||
<OptionRow class="number-input" title="Game resolution">
|
||||
<InputNumber
|
||||
class="shrink"
|
||||
size="small"
|
||||
:min="480"
|
||||
:max="9999"
|
||||
:use-grouping="false"
|
||||
v-model="prf.current!.display.rez[0]"
|
||||
/>
|
||||
x
|
||||
<InputNumber
|
||||
class="shrink"
|
||||
size="small"
|
||||
:min="640"
|
||||
:max="9999"
|
||||
:use-grouping="false"
|
||||
v-model="prf.current!.display.rez[1]"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow title="Display mode">
|
||||
<SelectButton
|
||||
v-model="prf.current!.display.mode"
|
||||
:options="[
|
||||
{ title: 'Window', value: 'Window' },
|
||||
{ title: 'Borderless window', value: 'Borderless' },
|
||||
{ title: 'Fullscreen', value: 'Fullscreen' },
|
||||
]"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Display rotation"
|
||||
v-if="capabilities.includes('display')"
|
||||
>
|
||||
<SelectButton
|
||||
v-model="prf.current!.display.rotation"
|
||||
:options="[
|
||||
{ title: 'Unchanged', value: 0 },
|
||||
{ title: 'Portrait', value: 90 },
|
||||
{ title: 'Portrait (flipped)', value: 270 },
|
||||
]"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
:disabled="extraDisplayOptionsDisabled"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
v-if="capabilities.includes('display')"
|
||||
class="number-input"
|
||||
title="Refresh Rate"
|
||||
>
|
||||
<InputNumber
|
||||
class="shrink"
|
||||
size="small"
|
||||
:min="60"
|
||||
:max="999"
|
||||
:use-grouping="false"
|
||||
v-model="prf.current!.display.frequency"
|
||||
:disabled="extraDisplayOptionsDisabled"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Borderless fullscreen"
|
||||
v-if="capabilities.includes('display')"
|
||||
tooltip="Match display resolution with the game."
|
||||
>
|
||||
<ToggleSwitch
|
||||
:disabled="
|
||||
extraDisplayOptionsDisabled ||
|
||||
prf.current?.display.mode !== 'Borderless'
|
||||
"
|
||||
v-model="prf.current!.display.borderless_fullscreen"
|
||||
/>
|
||||
</OptionRow>
|
||||
</OptionCategory>
|
||||
<OptionCategory title="Network">
|
||||
<OptionRow title="Network type">
|
||||
<SelectButton
|
||||
v-model="prf.current!.network.network_type"
|
||||
:options="[
|
||||
{ title: 'Remote', value: 'Remote' },
|
||||
{ title: 'Local (ARTEMiS)', value: 'Artemis' },
|
||||
]"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
v-if="prf.current!.network.network_type == 'Artemis'"
|
||||
title="ARTEMiS path"
|
||||
>
|
||||
<FilePicker
|
||||
:directory="false"
|
||||
promptname="index.py"
|
||||
extension="py"
|
||||
:value="prf.current!.network.local_path"
|
||||
:callback="
|
||||
(value: string) => (prf.current!.network.local_path = value)
|
||||
"
|
||||
></FilePicker>
|
||||
</OptionRow>
|
||||
<!-- <OptionRow
|
||||
v-if="prf.current!.network.network_type == 'Artemis'"
|
||||
title="ARTEMiS console"
|
||||
>
|
||||
<ToggleSwitch v-model="prf.current!.network.local_console" />
|
||||
</OptionRow> -->
|
||||
<OptionRow
|
||||
v-if="prf.current!.network.network_type == 'Remote'"
|
||||
title="Server address"
|
||||
>
|
||||
<InputText
|
||||
class="shrink"
|
||||
size="small"
|
||||
:maxlength="40"
|
||||
placeholder="192.168.1.234"
|
||||
v-model="prf.current!.network.remote_address"
|
||||
/> </OptionRow
|
||||
><OptionRow
|
||||
v-if="prf.current!.network.network_type == 'Remote'"
|
||||
title="Keychip"
|
||||
>
|
||||
<InputText
|
||||
class="shrink"
|
||||
size="small"
|
||||
:maxlength="16"
|
||||
placeholder="A123-01234567890"
|
||||
v-model="prf.current!.network.keychip"
|
||||
/> </OptionRow
|
||||
><OptionRow title="Subnet">
|
||||
<InputText
|
||||
class="shrink"
|
||||
size="small"
|
||||
:maxlength="15"
|
||||
placeholder="192.168.1.0"
|
||||
v-model="prf.current!.network.subnet"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow title="Address suffix">
|
||||
<InputNumber
|
||||
class="shrink"
|
||||
size="small"
|
||||
:maxlength="3"
|
||||
:min="0"
|
||||
:max="255"
|
||||
placeholder="12"
|
||||
v-model="prf.current!.network.suffix"
|
||||
/>
|
||||
</OptionRow>
|
||||
</OptionCategory>
|
||||
<OptionCategory title="Aime">
|
||||
<OptionRow title="Aime emulation">
|
||||
<Select
|
||||
v-model="prf.current!.sgt.aime"
|
||||
:options="[
|
||||
{ title: 'none', value: 'Disabled' },
|
||||
{ title: 'segatools built-in', value: 'BuiltIn' },
|
||||
...pkgs.aimes.map((p) => {
|
||||
return {
|
||||
title: pkgKey(p),
|
||||
value: hasFeature(p, Feature.AMNet)
|
||||
? { AMNet: pkgKey(p) }
|
||||
: { Other: pkgKey(p) },
|
||||
};
|
||||
}),
|
||||
]"
|
||||
placeholder="none"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
></Select>
|
||||
</OptionRow>
|
||||
<OptionRow title="Aime code">
|
||||
<InputText
|
||||
class="shrink"
|
||||
size="small"
|
||||
:disabled="prf.current!.sgt.aime === 'Disabled'"
|
||||
:maxlength="20"
|
||||
placeholder="00000000000000000000"
|
||||
v-model="aimeCodeModel"
|
||||
/>
|
||||
</OptionRow>
|
||||
<div v-if="prf.current!.sgt.aime?.hasOwnProperty('AMNet')">
|
||||
<OptionRow title="Server name">
|
||||
<InputText
|
||||
class="shrink"
|
||||
size="small"
|
||||
placeholder="CHUNI-PENGUIN"
|
||||
:maxlength="50"
|
||||
v-model="prf.current!.sgt.amnet.name"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow title="Server address">
|
||||
<InputText
|
||||
class="shrink"
|
||||
size="small"
|
||||
placeholder="http://+:6070"
|
||||
:maxlength="50"
|
||||
v-model="prf.current!.sgt.amnet.addr"
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow
|
||||
title="Use AiMeDB for physical cards"
|
||||
tooltip="Whether physical cards should use AiMeDB to retrieve access codes. If the game is using a hosted network, enable this option to load the same account data/profile as you would get on a physical cab."
|
||||
>
|
||||
<ToggleSwitch v-model="prf.current!.sgt.amnet.physical" />
|
||||
</OptionRow>
|
||||
</div>
|
||||
</OptionCategory>
|
||||
<OptionCategory title="Misc">
|
||||
<OptionRow title="OpenSSL bug workaround for Intel ≥10th gen">
|
||||
<ToggleSwitch v-model="prf.current!.sgt.intel" />
|
||||
</OptionRow>
|
||||
<OptionRow title="More segatools options">
|
||||
<FileEditor filename="segatools-base.ini" />
|
||||
</OptionRow>
|
||||
</OptionCategory>
|
||||
<OptionCategory title="Extensions">
|
||||
<SegatoolsOptions />
|
||||
<DisplayOptions v-if="prf.current!.meta.game === 'ongeki'" />
|
||||
<NetworkOptions />
|
||||
<AimeOptions />
|
||||
<MiscOptions />
|
||||
<OptionCategory
|
||||
title="Extensions"
|
||||
v-if="prf.current!.meta.game === 'ongeki'"
|
||||
>
|
||||
<OptionRow title="Inohara config">
|
||||
<FileEditor
|
||||
filename="inohara.cfg"
|
||||
@ -402,7 +33,8 @@ const extraDisplayOptionsDisabled = computed(() => {
|
||||
/>
|
||||
</OptionRow>
|
||||
<OptionRow title="BepInEx console">
|
||||
<ToggleSwitch v-model="prf.current!.bepinex.console" />
|
||||
<!-- @vue-expect-error -->
|
||||
<ToggleSwitch v-model="prf.current!.data.bepinex.console" />
|
||||
</OptionRow>
|
||||
</OptionCategory>
|
||||
</template>
|
||||
|
Reference in New Issue
Block a user