Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
2e17e0ae75 | |||
edef5cc6dc | |||
2dad0de4f1 | |||
14a65eb5bb |
13
CHANGELOG.md
13
CHANGELOG.md
@ -1,3 +1,16 @@
|
||||
## 0.18.3
|
||||
|
||||
- Updated Rainycolor's domain・真
|
||||
|
||||
## 0.18.2
|
||||
|
||||
- Updated Rainycolor's domain
|
||||
|
||||
## 0.18.1
|
||||
|
||||
- Keys can now be unbinded with Esc
|
||||
- Fixed CHUNITHM IR behavior on actual keyboards
|
||||
|
||||
## 0.18.0
|
||||
|
||||
- Added new grouping options to the package list
|
||||
|
@ -9,7 +9,7 @@ This is a program that seeks to streamline game data configuration, currently su
|
||||
|
||||
STARTLINER is four things:
|
||||
|
||||
- a mod installer and updater, powered by [Rainycolor Watercolor](https://rainy.patafour.zip),
|
||||
- a mod installer and updater, powered by [Rainycolor Watercolor](https://rainycolor.org),
|
||||
- a configuration GUI for segatools,
|
||||
- a glorified `start.bat` clicker, with automatic monitor setup and rollback,
|
||||
- [an abstraction allowing data configuration without touching the game directory](https://gitea.tendokyu.moe/akanyan/STARTLINER/wiki/Architecture-details).
|
||||
|
@ -480,13 +480,18 @@ pub async fn create_shortcut(_app: AppHandle, profile_meta: ProfileMeta) -> Resu
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn export_profile(state: State<'_, Mutex<AppData>>, export_keychip: bool, files: Vec<String>) -> Result<(), String> {
|
||||
pub async fn export_profile(
|
||||
state: State<'_, Mutex<AppData>>,
|
||||
is_diagnostic: bool,
|
||||
export_keychip: bool,
|
||||
files: Vec<String>
|
||||
) -> Result<(), String> {
|
||||
log::debug!("invoke: export_profile({:?}, {:?} files)", export_keychip, files.len());
|
||||
|
||||
let appd = state.lock().await;
|
||||
match &appd.profile {
|
||||
Some(p) => {
|
||||
p.export(export_keychip, files)
|
||||
p.export(export_keychip, files, is_diagnostic)
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
None => {
|
||||
|
@ -117,12 +117,16 @@ impl Keyboard {
|
||||
}
|
||||
}
|
||||
Keyboard::Chunithm(kb) => {
|
||||
let mut enabled_ir = false;
|
||||
if kb.enabled {
|
||||
for (i, cell) in kb.cell.iter().enumerate() {
|
||||
ini.with_section(Some("slider")).set(format!("cell{}", i + 1), cell.to_string());
|
||||
}
|
||||
for (i, ir) in kb.ir.iter().enumerate() {
|
||||
ini.with_section(Some("ir")).set(format!("ir{}", i + 1), ir.to_string());
|
||||
ini.with_section(Some("ir")).set(format!("ir{}", i + 1), (*ir).to_string());
|
||||
if i > 0 && *ir != 0 {
|
||||
enabled_ir = true;
|
||||
}
|
||||
}
|
||||
ini.with_section(Some("io3"))
|
||||
.set("test", kb.test.to_string())
|
||||
@ -140,8 +144,13 @@ impl Keyboard {
|
||||
.set("service", "0")
|
||||
.set("coin", "0");
|
||||
}
|
||||
ini.with_section(Some("io3"))
|
||||
.set("ir", "0");
|
||||
if enabled_ir {
|
||||
ini.with_section(Some("io3"))
|
||||
.set("ir", "0");
|
||||
} else {
|
||||
ini.with_section(Some("io3"))
|
||||
.set("ir", kb.ir[0].to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,6 +29,10 @@ impl PatchFileVec {
|
||||
}
|
||||
|
||||
pub fn find_patches(&self, target: impl AsRef<Path>) -> Result<Vec<Patch>> {
|
||||
if !target.as_ref().exists() {
|
||||
log::warn!("invalid target path: {:?}", target.as_ref());
|
||||
anyhow::bail!("Unable to open {:?}. Make sure the game path is correct.", target.as_ref());
|
||||
}
|
||||
let checksum = try_digest(target.as_ref())?;
|
||||
|
||||
let mut res_patches = Vec::new();
|
||||
|
@ -109,7 +109,7 @@ impl Package {
|
||||
loc: None,
|
||||
rmt: Some(Remote {
|
||||
package_url: p.package_url,
|
||||
download_url: v.download_url,
|
||||
download_url: v.download_url.replace("https://rainy.patafour.zip/", "https://www.rainycolor.org/"),
|
||||
icon: v.icon,
|
||||
deprecated: p.is_deprecated,
|
||||
nsfw: p.has_nsfw_content,
|
||||
|
@ -132,7 +132,7 @@ impl PackageStore {
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
let response = reqwest::get(format!("https://rainy.patafour.zip/c/{game}/api/v1/package/")).await?;
|
||||
let response = reqwest::get(format!("https://www.rainycolor.org/c/{game}/api/v1/package/")).await?;
|
||||
|
||||
let reader = response
|
||||
.bytes_stream()
|
||||
|
@ -36,7 +36,7 @@ impl Profile {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn export(&self, export_keychip: bool, extra_files: Vec<String>) -> anyhow::Result<()> {
|
||||
pub fn export(&self, export_keychip: bool, extra_files: Vec<String>, is_diagnostic: bool) -> anyhow::Result<()> {
|
||||
let mut prf = self.clone();
|
||||
|
||||
let dir = util::config_dir().join("exports");
|
||||
@ -45,7 +45,7 @@ impl Profile {
|
||||
std::fs::create_dir(&dir)?;
|
||||
}
|
||||
|
||||
let path = dir.join(format!("{}-{}-template.zip", &self.meta.game, &self.meta.name));
|
||||
let path = dir.join(format!("{}-{}-{}.zip", &self.meta.game, &self.meta.name, if is_diagnostic { "diagnostic" } else { "template" } ));
|
||||
|
||||
{
|
||||
let sgt = &mut prf.data.sgt;
|
||||
@ -66,7 +66,7 @@ impl Profile {
|
||||
if network.local_path.is_absolute() {
|
||||
network.local_path = PathBuf::new();
|
||||
}
|
||||
if !export_keychip {
|
||||
if !export_keychip || is_diagnostic {
|
||||
network.keychip = String::new();
|
||||
}
|
||||
}
|
||||
@ -83,6 +83,29 @@ impl Profile {
|
||||
zip.write_all(&std::fs::read(self.config_dir().join(file))?)?;
|
||||
}
|
||||
|
||||
if is_diagnostic {
|
||||
let name = "mu3.exe.log";
|
||||
let path = self.data_dir().join(name);
|
||||
if path.exists() {
|
||||
zip.start_file(name, options)?;
|
||||
zip.write_all(&std::fs::read(path)?)?;
|
||||
}
|
||||
|
||||
let name = "chusanApp.exe.log";
|
||||
let path = self.data_dir().join(name);
|
||||
if path.exists() {
|
||||
zip.start_file(name, options)?;
|
||||
zip.write_all(&std::fs::read(path)?)?;
|
||||
}
|
||||
|
||||
let name = "amdaemon.exe.log";
|
||||
let path = self.data_dir().join(name);
|
||||
if path.exists() {
|
||||
zip.start_file(name, options)?;
|
||||
zip.write_all(&std::fs::read(path)?)?;
|
||||
}
|
||||
}
|
||||
|
||||
zip.finish()?;
|
||||
|
||||
Ok(())
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "STARTLINER",
|
||||
"version": "0.18.0",
|
||||
"version": "0.19.0",
|
||||
"identifier": "zip.patafour.startliner",
|
||||
"build": {
|
||||
"beforeDevCommand": "bun run dev",
|
||||
|
@ -4,6 +4,9 @@ import InputText from 'primevue/inputtext';
|
||||
import { fromKeycode, toKeycode } from '../keyboard';
|
||||
import { usePrfStore } from '../stores';
|
||||
import { OngekiButtons } from '../types';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const prf = usePrfStore();
|
||||
|
||||
@ -61,6 +64,10 @@ const handleKey = (
|
||||
}
|
||||
}
|
||||
|
||||
if (event.code === 'Escape') {
|
||||
keycode = 0;
|
||||
}
|
||||
|
||||
if (index !== undefined) {
|
||||
data[button][index] = keycode;
|
||||
} else {
|
||||
@ -160,13 +167,24 @@ const fontSize = computed(() => {
|
||||
<InputText
|
||||
:style="{
|
||||
width: small ? '2.8rem' : '5rem',
|
||||
height: small ? '2.8rem' : tall ? '10rem' : '5rem',
|
||||
height:
|
||||
small && tall
|
||||
? '5rem'
|
||||
: small
|
||||
? '2.8rem'
|
||||
: tall
|
||||
? '10rem'
|
||||
: '5rem',
|
||||
fontSize,
|
||||
backgroundColor: color,
|
||||
}"
|
||||
unstyled
|
||||
class="text-center buttoninputtext"
|
||||
v-tooltip="tooltip ? `${tooltip}: ${modelValue}` : undefined"
|
||||
v-tooltip="
|
||||
tooltip
|
||||
? `${tooltip}: ${modelValue} ${tooltip.startsWith('ir') ? `\n${t('cfg.keyboard.irTooltip')}` : ''}`
|
||||
: undefined
|
||||
"
|
||||
@contextmenu.prevent="() => {}"
|
||||
@keydown="(ev: KeyboardEvent) => handleKey(button, ev, index)"
|
||||
@mousedown="
|
||||
|
@ -29,23 +29,34 @@ const files = new Set<string>();
|
||||
).includes('chunithm');
|
||||
})();
|
||||
|
||||
const fileList = {
|
||||
ongeki: ['aime.txt', 'inohara.cfg', 'mu3.ini', 'segatools-base.ini'],
|
||||
chunithm: ['aime.txt', 'saekawa.toml', 'segatools-base.ini'],
|
||||
};
|
||||
|
||||
const diagnosticList = {
|
||||
ongeki: ['mu3.ini', 'segatools-base.ini'],
|
||||
chunithm: ['segatools-base.ini'],
|
||||
};
|
||||
|
||||
const diagnostic = ref(false);
|
||||
|
||||
const exportTemplate = async () => {
|
||||
const fl = [...files.values()];
|
||||
exportVisible.value = false;
|
||||
await invoke('export_profile', {
|
||||
exportKeychip: exportKeychip.value,
|
||||
files: fl,
|
||||
isDiagnostic: diagnostic.value,
|
||||
files:
|
||||
diagnostic.value === true
|
||||
? diagnosticList[prf.current!.meta.game]
|
||||
: fl,
|
||||
});
|
||||
await invoke('open_file', {
|
||||
path: await path.join(await general.configDir, 'exports'),
|
||||
});
|
||||
};
|
||||
|
||||
const fileList = {
|
||||
ongeki: ['aime.txt', 'inohara.cfg', 'mu3.ini', 'segatools-base.ini'],
|
||||
chunithm: ['aime.txt', 'saekawa.toml', 'segatools-base.ini'],
|
||||
};
|
||||
|
||||
const fileListCurrent: Ref<string[]> = ref([]);
|
||||
|
||||
const recalcFileList = async () => {
|
||||
@ -91,16 +102,36 @@ const importPick = async () => {
|
||||
:visible="exportVisible"
|
||||
:closable="false /*this shit doesn't work */"
|
||||
:header="`${t('profile.export')} ${prf.current?.meta.name}`"
|
||||
:style="{ width: '300px', scale: client.scaleValue }"
|
||||
:style="{ width: '330px', scale: client.scaleValue }"
|
||||
>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col items-center">
|
||||
<SelectButton
|
||||
v-model="diagnostic"
|
||||
:options="[
|
||||
{
|
||||
title: t('profile.standardExport'),
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
title: t('profile.diagnostic'),
|
||||
value: true,
|
||||
},
|
||||
]"
|
||||
:allow-empty="false"
|
||||
option-label="title"
|
||||
option-value="value"
|
||||
>
|
||||
</SelectButton>
|
||||
</div>
|
||||
<div class="flex flex-row">
|
||||
<div class="grow">{{ t('profile.export') }} keychip</div>
|
||||
<ToggleSwitch v-model="exportKeychip" />
|
||||
<ToggleSwitch :disabled="diagnostic" v-model="exportKeychip" />
|
||||
</div>
|
||||
<div class="flex flex-row" v-for="f in fileListCurrent">
|
||||
<div class="grow">{{ t('profile.export') }} {{ f }}</div>
|
||||
<ToggleSwitch
|
||||
:disabled="diagnostic"
|
||||
:model-value="true"
|
||||
@update:model-value="
|
||||
(v) => {
|
||||
|
@ -95,7 +95,7 @@ const prf = usePrfStore();
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="prf.current?.meta.game === 'chunithm'">
|
||||
<div class="absolute left-1/2 top-1/5">
|
||||
<div class="absolute left-9/17 top-1/12">
|
||||
<div
|
||||
class="flex flex-row flex-nowrap gap-2 self-center w-full"
|
||||
>
|
||||
@ -108,6 +108,7 @@ const prf = usePrfStore();
|
||||
button="ir"
|
||||
:index="idx - 1"
|
||||
:tooltip="`ir${idx}`"
|
||||
tall
|
||||
small
|
||||
color="rgba(0, 255, 0, 0.2)"
|
||||
/>
|
||||
|
@ -44,8 +44,10 @@ export default {
|
||||
reallyDelete: 'Are you sure you want to delete {profile}?',
|
||||
template: 'STARTLINER template',
|
||||
importTemplate: 'Import template',
|
||||
exportTemplate: 'Export template',
|
||||
exportTemplate: 'Export profile',
|
||||
export: 'Export',
|
||||
standardExport: 'Template',
|
||||
diagnostic: 'Diagnostic',
|
||||
},
|
||||
creator: {
|
||||
header: 'Package creator',
|
||||
@ -189,6 +191,8 @@ export default {
|
||||
'Only applicable if the IO module is set to segatools built-in (keyboard) or a compatible third-party module (like mu3io.NET)',
|
||||
leverMode: 'Lever mode',
|
||||
mouse: 'Mouse',
|
||||
irTooltip:
|
||||
'When playing on an actual keyboard, only bind ir1; leave the rest unbound',
|
||||
},
|
||||
wine: {
|
||||
prefix: 'Wine prefix',
|
||||
|
@ -44,8 +44,10 @@ export default {
|
||||
reallyDelete: 'Czy na pewno chcesz usunąć {profile}?',
|
||||
template: 'Szablon',
|
||||
importTemplate: 'Importuj szablon',
|
||||
exportTemplate: 'Eksportuj szablon',
|
||||
exportTemplate: 'Eksportuj profil',
|
||||
export: 'Eksportuj',
|
||||
standardExport: 'Szablon',
|
||||
diagnostic: 'Diagnostyka',
|
||||
},
|
||||
creator: {
|
||||
header: 'Kreator pakietów',
|
||||
@ -229,6 +231,8 @@ export default {
|
||||
'Dotyczy tylko wtedy, gdy moduł IO jest ustawiony na wbudowaną emulację lub zgodny moduł (np. mu3io.NET)',
|
||||
leverMode: 'Tryb wajchy',
|
||||
mouse: 'Mysz',
|
||||
irTooltip:
|
||||
'Jeśli grasz na klawiaturze, ustaw tylko ir1; pozostałe zostaw wyłączone',
|
||||
},
|
||||
wine: {
|
||||
prefix: 'Wine prefix',
|
||||
|
Reference in New Issue
Block a user