forked from akanyan/STARTLINER
feat: hex patches
This commit is contained in:
@ -1,3 +1,7 @@
|
|||||||
|
## 0.14.0
|
||||||
|
|
||||||
|
- Added the custom FREE PLAY patch for Verse
|
||||||
|
|
||||||
## 0.13.0
|
## 0.13.0
|
||||||
|
|
||||||
- Added profile imports/exports
|
- Added profile imports/exports
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
Looking for maimai DX players willing to help develop/test maimai DX support
|
Looking for
|
||||||
|
|
||||||
|
- maimai DX players willing to help develop/test maimai DX support
|
||||||
|
- translators (any language other than English)
|
||||||
|
|
||||||
# STARTLINER
|
# STARTLINER
|
||||||
|
|
||||||
|
1
TODO.md
1
TODO.md
@ -1,5 +1,6 @@
|
|||||||
### Short-term
|
### Short-term
|
||||||
|
|
||||||
|
- i18n
|
||||||
- https://gitea.tendokyu.moe/TeamTofuShop/segatools/issues/63
|
- https://gitea.tendokyu.moe/TeamTofuShop/segatools/issues/63
|
||||||
|
|
||||||
### Long-term
|
### Long-term
|
||||||
|
@ -431,7 +431,7 @@ pub async fn export_profile(state: State<'_, Mutex<AppData>>, export_keychip: bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn import_profile(state: State<'_, Mutex<AppData>>, path: PathBuf) -> Result<(), String> {
|
pub async fn import_profile(path: PathBuf) -> Result<(), String> {
|
||||||
log::debug!("invoke: import_profile({:?})", path);
|
log::debug!("invoke: import_profile({:?})", path);
|
||||||
|
|
||||||
Profile::import(path).map_err(|e| e.to_string())
|
Profile::import(path).map_err(|e| e.to_string())
|
||||||
|
@ -42,6 +42,7 @@ pub struct Patch {
|
|||||||
pub enum PatchData {
|
pub enum PatchData {
|
||||||
Normal(NormalPatch),
|
Normal(NormalPatch),
|
||||||
Number(NumberPatch),
|
Number(NumberPatch),
|
||||||
|
Hex(HexPatch),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||||
@ -65,6 +66,12 @@ pub struct NumberPatch {
|
|||||||
pub max: i32
|
pub max: i32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||||
|
pub struct HexPatch {
|
||||||
|
pub offset: u64,
|
||||||
|
pub off: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Serialize for Patch {
|
impl Serialize for Patch {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where S: Serializer {
|
where S: Serializer {
|
||||||
@ -83,6 +90,11 @@ impl Serialize for Patch {
|
|||||||
state.serialize_field("size", &patch.size)?;
|
state.serialize_field("size", &patch.size)?;
|
||||||
state.serialize_field("min", &patch.min)?;
|
state.serialize_field("min", &patch.min)?;
|
||||||
state.serialize_field("max", &patch.max)?;
|
state.serialize_field("max", &patch.max)?;
|
||||||
|
},
|
||||||
|
PatchData::Hex(patch) => {
|
||||||
|
state.serialize_field("type", "hex")?;
|
||||||
|
state.serialize_field("offset", &patch.offset)?;
|
||||||
|
state.serialize_field("off", &patch.off)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.end()
|
state.end()
|
||||||
@ -114,6 +126,23 @@ impl<'de> serde::Deserialize<'de> for Patch {
|
|||||||
.ok_or_else(|| de::Error::missing_field("max"))?
|
.ok_or_else(|| de::Error::missing_field("max"))?
|
||||||
).map_err(|_| de::Error::missing_field("max"))?
|
).map_err(|_| de::Error::missing_field("max"))?
|
||||||
}),
|
}),
|
||||||
|
Some("hex") => {
|
||||||
|
let mut off_list = Vec::new();
|
||||||
|
for off in value.get("off").and_then(Value::as_array).unwrap() {
|
||||||
|
off_list.push(u8::try_from(
|
||||||
|
off.as_u64().ok_or_else(|| de::Error::missing_field("off"))?
|
||||||
|
).map_err(|_| de::Error::missing_field("off"))?);
|
||||||
|
}
|
||||||
|
// for off in value.get("off").and_then(Value::as_str).unwrap().bytes() {
|
||||||
|
// off_list.push(off);
|
||||||
|
// }
|
||||||
|
PatchData::Hex(HexPatch {
|
||||||
|
offset: value.get("offset")
|
||||||
|
.and_then(Value::as_u64)
|
||||||
|
.ok_or_else(|| de::Error::missing_field("offset"))?,
|
||||||
|
off: off_list
|
||||||
|
})
|
||||||
|
},
|
||||||
None => {
|
None => {
|
||||||
let mut patches = vec![];
|
let mut patches = vec![];
|
||||||
for patch in value.get("patches").and_then(Value::as_array).unwrap() {
|
for patch in value.get("patches").and_then(Value::as_array).unwrap() {
|
||||||
|
@ -50,6 +50,21 @@ impl PatchSelection {
|
|||||||
} else {
|
} else {
|
||||||
log::error!("invalid number patch {:?}", patch);
|
log::error!("invalid number patch {:?}", patch);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
PatchData::Hex(data) => {
|
||||||
|
if let PatchSelectionData::Hex(val) = sel {
|
||||||
|
res += &format!("{} F+{:X} ", filename, data.offset);
|
||||||
|
for byte in val {
|
||||||
|
res += &format!("{:02X}", byte);
|
||||||
|
}
|
||||||
|
res += " ";
|
||||||
|
for byte in &data.off {
|
||||||
|
res += &format!("{:02X}", byte);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::error!("invalid number patch {:?}", patch);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
format!("{}\n", res)
|
format!("{}\n", res)
|
||||||
|
@ -326,6 +326,26 @@
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'standard-custom-free-play-length',
|
||||||
|
type: 'number',
|
||||||
|
name: 'Custom FREE PLAY text length',
|
||||||
|
tooltip: 'Changes the length of the text displayed when Force FREE PLAY credit text is enabled',
|
||||||
|
danger: 'If this is longer than 11 characters, \"Force FREE PLAY credit text\" MUST be enabled.',
|
||||||
|
offset: 0x3875A9,
|
||||||
|
size: 1,
|
||||||
|
default: 9,
|
||||||
|
min: 0,
|
||||||
|
max: 27,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'standard-custom-free-play-text',
|
||||||
|
type: 'hex',
|
||||||
|
name: 'Custom FREE PLAY text',
|
||||||
|
tooltip: 'Replace the FREE PLAY text when using Infinite credits',
|
||||||
|
offset: 0x1A5DFB4,
|
||||||
|
off: [0x46, 0x52, 0x45, 0x45, 0x20, 0x50, 0x4c, 0x41, 0x59],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
import InputNumber from 'primevue/inputnumber';
|
import InputNumber from 'primevue/inputnumber';
|
||||||
|
import InputText from 'primevue/inputtext';
|
||||||
import ToggleSwitch from 'primevue/toggleswitch';
|
import ToggleSwitch from 'primevue/toggleswitch';
|
||||||
import OptionRow from './OptionRow.vue';
|
import OptionRow from './OptionRow.vue';
|
||||||
import { usePrfStore } from '../stores';
|
import { usePrfStore } from '../stores';
|
||||||
@ -23,9 +25,26 @@ const setNumber = (key: string, val: number) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
patch: Object as () => Patch,
|
patch: Object as () => Patch,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// One day, I will repent
|
||||||
|
const hexModel = computed({
|
||||||
|
get() {
|
||||||
|
const hex = (prf.current!.data.patches[props.patch!.id!] as any)?.hex;
|
||||||
|
if (hex !== undefined) {
|
||||||
|
return new TextDecoder().decode(new Int8Array(hex).buffer);
|
||||||
|
} else {
|
||||||
|
return 'FREE PLAY';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set(value: string) {
|
||||||
|
(prf.current!.data.patches[props.patch!.id!] as any) = {
|
||||||
|
hex: new TextEncoder().encode(value),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -50,5 +69,6 @@ defineProps({
|
|||||||
:max="patch?.max"
|
:max="patch?.max"
|
||||||
:placeholder="(patch?.default ?? 0).toString()"
|
:placeholder="(patch?.default ?? 0).toString()"
|
||||||
/>
|
/>
|
||||||
|
<InputText v-else-if="patch?.type === 'hex'" v-model="hexModel" />
|
||||||
</OptionRow>
|
</OptionRow>
|
||||||
</template>
|
</template>
|
||||||
|
@ -167,7 +167,7 @@ export interface Patch {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
tooltip: string;
|
tooltip: string;
|
||||||
type: undefined | 'number';
|
type: undefined | 'number' | 'hex';
|
||||||
default: number;
|
default: number;
|
||||||
min: number;
|
min: number;
|
||||||
max: number;
|
max: number;
|
||||||
|
Reference in New Issue
Block a user