fix: also add post scripts

This commit is contained in:
2025-05-09 21:45:59 +00:00
parent 469ba5f574
commit 67872bc4d1
12 changed files with 119 additions and 55 deletions

View File

@ -1,8 +1,13 @@
## 0.20.0 ## 0.20.1
- Added user-customizable pre-launch scripts
- Added japanese localization - Added japanese localization
- Fixed the file editor not updating state properly - Added user-customizable scripts
- The launch script runs directly before the game, and is equivalent to adding lines above `start "AM Daemon" ...` in start.bat
- The end script runs after the game has closed for any reason, and is equivalent to adding lines below `taskkill /f /im amdaemon.exe ...` in start.bat
- This functionality is needed for cursed things such as brokenithm
- Fixed the file editor not updating its state properly
- Fixed diagnostic exports not exporting paths
- Fixed "Install recommended packages" not enabling the segatools hook
## 0.19.1 ## 0.19.1

View File

@ -255,7 +255,7 @@ impl Profile {
let mut game_builder; let mut game_builder;
let mut amd_builder; let mut amd_builder;
let mut prelaunch = None; let mut prescript = None;
let target_path = PathBuf::from(&self.data.sgt.target); let target_path = PathBuf::from(&self.data.sgt.target);
let exe_dir = target_path.parent().ok_or_else(|| anyhow!("Invalid target path"))?; let exe_dir = target_path.parent().ok_or_else(|| anyhow!("Invalid target path"))?;
@ -265,17 +265,6 @@ impl Profile {
{ {
game_builder = Command::new(sgt_dir.join(self.meta.game.inject_exe())); game_builder = Command::new(sgt_dir.join(self.meta.game.inject_exe()));
amd_builder = Command::new("cmd.exe"); amd_builder = Command::new("cmd.exe");
let prelaunch_path = self.config_dir().join("prelaunch.bat");
if prelaunch_path.exists() {
let mut c = Command::new("cmd");
c.args(["/C", "start"]);
c.raw_arg("\"STARTLINER Prelaunch\"");
c.args(["cmd", "/C"]);
c.arg(prelaunch_path);
log::debug!("Prelaunch: {:?}", c);
prelaunch = Some(c.spawn()?);
}
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
{ {
@ -284,14 +273,12 @@ impl Profile {
game_builder.arg(sgt_dir.join(self.meta.game.inject_exe())); game_builder.arg(sgt_dir.join(self.meta.game.inject_exe()));
amd_builder.arg("cmd.exe"); amd_builder.arg("cmd.exe");
}
let prelaunch_path = self.config_dir().join("prelaunch.sh"); let script_ext = if cfg!(target_os = "windows") { "bat" } else { "sh" };
if prelaunch_path.exists() { let prescript_path = self.config_dir().join(format!("pre.{script_ext}"));
prelaunch_builder = Some(Command::new("sh")); if prescript_path.exists() {
c.arg(prelaunch_path); prescript = util::spawn_script(prescript_path, &exe_dir, "\"STARTLINER launch script\"");
log::debug!("Prelaunch: {:?}", c);
prelaunch = Some(c.spawn()?);
}
} }
amd_builder.env( amd_builder.env(
@ -440,11 +427,11 @@ impl Profile {
util::pkill("amdaemon.exe").await; util::pkill("amdaemon.exe").await;
} }
if let Some(mut _child) = prelaunch { if let Some(mut _child) = prescript {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
// child.kill() doesn't work // child.kill() doesn't work
util::pkill_title("STARTLINER Prelaunch").await; util::pkill_title("STARTLINER launch script").await;
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
{ {
@ -452,6 +439,11 @@ impl Profile {
} }
} }
let postscript_path = self.config_dir().join(format!("post.{script_ext}"));
if postscript_path.exists() {
_ = util::spawn_script(postscript_path, &exe_dir, "\"STARTLINER end script\"");
}
set.join_next().await.expect("No spawn").expect("No result"); set.join_next().await.expect("No spawn").expect("No result");
log::debug!("Fin"); log::debug!("Fin");

View File

@ -49,15 +49,17 @@ impl Profile {
{ {
let sgt = &mut prf.data.sgt; let sgt = &mut prf.data.sgt;
sgt.target = PathBuf::new(); if !is_diagnostic {
if sgt.amfs.is_absolute() { sgt.target = PathBuf::new();
sgt.amfs = PathBuf::new(); if sgt.amfs.is_absolute() {
} sgt.amfs = PathBuf::new();
if sgt.option.is_absolute() { }
sgt.option = PathBuf::new(); if sgt.option.is_absolute() {
} sgt.option = PathBuf::new();
if sgt.appdata.is_absolute() { }
sgt.appdata = PathBuf::new(); if sgt.appdata.is_absolute() {
sgt.appdata = PathBuf::new();
}
} }
} }

View File

@ -220,4 +220,38 @@ pub fn create_shortcut(
file.Save(Some(lnk_path.to_str().ok_or_else(|| anyhow!("Illegal shortcut path"))?), true)?; file.Save(Some(lnk_path.to_str().ok_or_else(|| anyhow!("Illegal shortcut path"))?), true)?;
Ok(()) Ok(())
}
pub fn spawn_script(path: impl AsRef<Path>, cwd: impl AsRef<Path>, title: &str) -> Option<tokio::process::Child> {
// Seems? like this autism is needed to:
// 1. pop up a cmd window
// 2. launch the batch
// 3. die afterwards
let mut c;
#[cfg(target_os = "windows")]
{
c = Command::new("cmd");
c.args(["/C", "start"]);
c.raw_arg(title);
c.args(["cmd", "/C"]);
c.arg(path.as_ref());
c.current_dir(cwd);
}
#[cfg(target_os = "linux")]
{
c = Command::new("sh");
c.arg(path.as_ref());
c.current_dir(cwd);
}
log::debug!("Script launch: {:?}", c);
match c.spawn() {
Ok(child) => Some(child),
Err(e) => {
log::error!("unable to launch {:?}: {e}", path.as_ref());
None
}
}
} }

View File

@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "STARTLINER", "productName": "STARTLINER",
"version": "0.20.0", "version": "0.20.1",
"identifier": "zip.patafour.startliner", "identifier": "zip.patafour.startliner",
"build": { "build": {
"beforeDevCommand": "bun run dev", "beforeDevCommand": "bun run dev",

View File

@ -12,8 +12,8 @@ invoke('get_changelog').then((s) => (changelog.value = s as string));
<template> <template>
<h1>About</h1> <h1>About</h1>
STARTLINER is a launcher, configuration tool and mod manager for STARTLINER is a configuration tool, mod manager and start.bat automation
O.N.G.E.K.I. and CHUNITHM. engine for O.N.G.E.K.I. and CHUNITHM.
<h1>Changelog</h1> <h1>Changelog</h1>
<ScrollPanel style="height: 200px"> <ScrollPanel style="height: 200px">
<div class="markdown"> <div class="markdown">

View File

@ -71,10 +71,12 @@ const recommendedTooltip = computed(() => {
const installRecommended = () => { const installRecommended = () => {
if (prf.current?.meta.game === 'ongeki') { if (prf.current?.meta.game === 'ongeki') {
pkgs.installFromKey('segatools-mu3hook'); pkgs.installFromKey('segatools-mu3hook');
prf.current.data.sgt.hook = 'segatools-mu3hook';
} }
if (prf.current?.meta.game === 'chunithm') { if (prf.current?.meta.game === 'chunithm') {
pkgs.installFromKey('segatools-chusanhook'); pkgs.installFromKey('segatools-chusanhook');
pkgs.installFromKey('mempatcher-mempatcher'); pkgs.installFromKey('mempatcher-mempatcher');
prf.current.data.sgt.hook = 'segatools-chusanhook';
} }
}; };
</script> </script>

View File

@ -29,10 +29,17 @@ const files = new Set<string>();
).includes('chunithm'); ).includes('chunithm');
})(); })();
const fileList = { const fileList = [
ongeki: ['aime.txt', 'inohara.cfg', 'mu3.ini', 'segatools-base.ini'], 'aime.txt',
chunithm: ['aime.txt', 'saekawa.toml', 'segatools-base.ini'], 'inohara.cfg',
}; 'saekawa.toml',
'mu3.ini',
'segatools-base.ini',
'pre.sh',
'pre.bat',
'post.sh',
'post.bat',
];
const diagnosticList = { const diagnosticList = {
ongeki: ['mu3.ini', 'segatools-base.ini'], ongeki: ['mu3.ini', 'segatools-base.ini'],
@ -62,8 +69,7 @@ const fileListCurrent: Ref<string[]> = ref([]);
const recalcFileList = async () => { const recalcFileList = async () => {
const res: string[] = []; const res: string[] = [];
files.clear(); files.clear();
for (const idx in fileList[prf.current!.meta.game]) { for (const f of fileList) {
const f = fileList[prf.current!.meta.game][idx];
const p = await path.join(await prf.configDir, f); const p = await path.join(await prf.configDir, f);
if (await invoke('file_exists', { path: p })) { if (await invoke('file_exists', { path: p })) {
res.push(f); res.push(f);

View File

@ -41,18 +41,33 @@ const prf = usePrfStore();
<FileEditor filename="segatools-base.ini" /> <FileEditor filename="segatools-base.ini" />
</OptionRow> </OptionRow>
<OptionRow <OptionRow
:title="t('cfg.misc.prelaunch')" :title="t('cfg.misc.prescript')"
:tooltip="t('cfg.misc.prelaunchTooltip')" :tooltip="t('cfg.misc.prescriptTooltip')"
> >
<FileEditor <FileEditor
v-if="extension === 'bat'" v-if="extension === 'bat'"
filename="prelaunch.bat" filename="pre.bat"
:defaultValue="`@echo off\n\nREM This script will be launched alongside the game\n`" :defaultValue="`@echo off\n\nREM This script will launch before (and alongside) the game\n`"
/> />
<FileEditor <FileEditor
v-else-if="extension === 'sh'" v-else-if="extension === 'sh'"
filename="prelaunch.sh" filename="pre.sh"
:defaultValue="`#!/bin/sh\n\n# This script will be launched alongside the game\n`" :defaultValue="`#!/bin/sh\n\n# This script will launch before (and alongside) the game\n`"
/>
</OptionRow>
<OptionRow
:title="t('cfg.misc.postscript')"
:tooltip="t('cfg.misc.postscriptTooltip')"
>
<FileEditor
v-if="extension === 'bat'"
filename="post.bat"
:defaultValue="`@echo off\n\nREM This script will launch after the game has died\n`"
/>
<FileEditor
v-else-if="extension === 'sh'"
filename="post.sh"
:defaultValue="`#!/bin/sh\n\n# This script will launch after the game has died\n`"
/> />
</OptionRow> </OptionRow>
</OptionCategory> </OptionCategory>

View File

@ -166,8 +166,11 @@ export default {
other: 'Other segatools options', other: 'Other segatools options',
otherTooltip: otherTooltip:
'Advanced or situational options not covered by STARTLINER', 'Advanced or situational options not covered by STARTLINER',
prelaunch: 'Prelaunch script', prescript: 'Launch script',
prelaunchTooltip: 'Optional script that runs before the game.', prescriptTooltip: 'Optional script that runs before the game.',
postscript: 'End script',
postscriptTooltip:
'Optional script that runs after the game has ended.',
}, },
extensions: { extensions: {
title: 'Extensions', title: 'Extensions',

View File

@ -144,7 +144,7 @@ export default {
general: '一般', general: '一般',
builtIn: 'segatools内蔵エミュレーション', builtIn: 'segatools内蔵エミュレーション',
targetTooltip: targetTooltip:
'STARTLINERはそれ以外のクリーンなデータに解凍された実行可能ファイルを期待する。', 'STARTLINERはそれ以外のクリーンなデータにクラック実行可能ファイルを期待する。',
hooks: 'フック', hooks: 'フック',
ioModules: 'IOモジュール', ioModules: 'IOモジュール',
ioModulesDesc: 'これは望ましい入力方法と一致するはずです。', ioModulesDesc: 'これは望ましい入力方法と一致するはずです。',
@ -206,8 +206,10 @@ export default {
intelTooltip: '代わりにamdaemonにパッチを当てることを推奨する。', intelTooltip: '代わりにamdaemonにパッチを当てることを推奨する。',
other: 'その他segatools設定', other: 'その他segatools設定',
otherTooltip: 'STARTLINERに含まれない上級者向けまたは状況別設定', otherTooltip: 'STARTLINERに含まれない上級者向けまたは状況別設定',
prelaunch: 'スタート前スクリプト', prescript: '起動前のスクリプト',
prelaunchTooltip: 'ゲームの前に実行されるスクリプト', prescriptTooltip: 'ゲームの前に実行されるスクリプト',
postscript: '終了後のスクリプト',
postscriptTooltip: 'ゲーム終了後に実行されるスクリプト',
}, },
extensions: { extensions: {
title: 'エクステンション', title: 'エクステンション',

View File

@ -206,8 +206,11 @@ export default {
other: 'Inne opcje segatools', other: 'Inne opcje segatools',
otherTooltip: otherTooltip:
'Zaawansowane lub sytuacyjne opcje, które nie są objęte przez STARTLINERA', 'Zaawansowane lub sytuacyjne opcje, które nie są objęte przez STARTLINERA',
prelaunch: 'Skrypt przedstartowy', prescript: 'Skrypt startowy',
prelaunchTooltip: 'Opcjonalny skrypt uruchamiany przed grą.', prescriptTooltip: 'Opcjonalny skrypt uruchamiany przed grą.',
postscript: 'Skrypt końcowy',
postscriptTooltip:
'Opcjonalny skrypt uruchamiany po zakończeniu gry.',
}, },
extensions: { extensions: {
title: 'Rozszerzenia', title: 'Rozszerzenia',