Files
STARTLINER/src/components/OptionList.vue

332 lines
11 KiB
Vue

<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 { usePrfStore } from '../stores';
const prf = usePrfStore();
const aimeCode = ref('');
const capabilities: Ref<string[]> = ref([]);
const displayList: Ref<{ title: string; value: string }[]> = ref([
{
title: 'Primary',
value: 'default',
},
]);
const hookList: Ref<{ title: string; value: string }[]> = ref([
{
title: 'segatools-mu3hook',
value: 'segatools-mu3hook',
},
]);
invoke('list_platform_capabilities')
.then(async (v: unknown) => {
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]>) {
displayList.value.push({
title: `${devName.replace('\\\\.\\', '')} (${devString})`,
value: devName,
});
}
}
})
.catch(() => {});
const aimeCodeModel = computed({
get() {
return aimeCode.value;
},
async set(value: string) {
aimeCode.value = value;
if (value.match(/^[0-9]{20}$/)) {
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="mu3hook">
<Select
model-value="segatools-mu3hook"
:options="hookList"
option-label="title"
option-value="value"
></Select>
</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>
</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"
></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="Match display resolution with the game"
v-if="capabilities.includes('display')"
>
<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="Misc">
<OptionRow title="OpenSSL bug workaround for Intel ≥10th gen">
<ToggleSwitch v-model="prf.current!.sgt.intel" />
</OptionRow>
<OptionRow title="Aime emulation">
<ToggleSwitch v-model="prf.current!.sgt.enable_aime" />
</OptionRow>
<OptionRow title="Aime code">
<InputText
class="shrink"
size="small"
:disabled="prf.current?.sgt.enable_aime !== true"
:maxlength="20"
placeholder="00000000000000000000"
v-model="aimeCodeModel"
/>
</OptionRow>
<OptionRow title="More segatools options">
<FileEditor filename="segatools-base.ini" />
</OptionRow>
</OptionCategory>
<OptionCategory title="Extensions">
<OptionRow title="Inohara config">
<FileEditor
filename="inohara.cfg"
promptname="inohara config file"
extension="cfg"
/>
</OptionRow>
<OptionRow title="BepInEx console">
<ToggleSwitch v-model="prf.current!.bepinex.console" />
</OptionRow>
</OptionCategory>
</template>
<style>
.number-input .p-inputnumber-input {
width: 4rem;
}
.p-inputtext {
font-family: monospace;
}
</style>