Last active
March 13, 2024 13:54
-
-
Save Coolsonickirby/1ebfb8e59d4121376b0a95a0de2de2a3 to your computer and use it in GitHub Desktop.
former wip audio table expansion (shoutouts to jozz for telling me about the easier way)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use crate::api::cpu::*; | |
use once_cell::sync::Lazy; | |
use skyline::hooks::{getRegionAddress, InlineCtx, Region}; | |
use std::collections::HashMap; | |
#[repr(C)] | |
#[derive(Debug, Copy, Clone)] | |
pub struct NarrationCharacallEntry { | |
pub unk_1: u64, | |
pub nus3bank_path: u64, | |
pub tonelabel_path: u64, | |
pub nus3audio_path: u64, | |
pub unk_2: u64, | |
} | |
// this is gonna be a wip for a while | |
pub static mut NARRATION_CHARACALL_TABLE: Lazy<NarrationCharacallTable> = | |
Lazy::new(|| NarrationCharacallTable::new(0x4545230, 0x21)); | |
// (ptr, data) | |
pub static mut HOOK_DATA_FOR_ID: Lazy<HashMap<u16, Data>> = Lazy::new(|| HashMap::new()); | |
pub struct NarrationCharacallTable { | |
pub offset: usize, | |
pub refs: Vec<PatchLocation>, | |
pub table: Vec<NarrationCharacallEntry>, | |
} | |
#[derive(Debug)] | |
pub struct Data { | |
pub ptr: usize, | |
pub register: u8, | |
} | |
impl<'a, 'b> PartialEq<Data> for Data { | |
fn eq(&self, other: &Data) -> bool { | |
self.ptr == other.ptr && self.register == other.register | |
} | |
} | |
static mut DATA_ID: u16 = 0; | |
fn get_data_id() -> u16 { | |
unsafe { | |
let d = DATA_ID; | |
DATA_ID += 1; | |
d | |
} | |
} | |
pub fn get_id_if_data_exists(data: &Data) -> Option<u16> { | |
let mut r: Option<u16> = None; | |
unsafe { | |
for (key, value) in &*HOOK_DATA_FOR_ID { | |
if *data == *value { | |
r = Some(*key); | |
break; | |
} | |
} | |
} | |
r | |
} | |
pub fn insert_data(data: Data) -> u16 { | |
unsafe { | |
let id = get_data_id(); | |
HOOK_DATA_FOR_ID.insert(id, data); | |
id | |
} | |
} | |
pub struct MovZ { | |
pub imm16: u32, | |
pub rd: u8, | |
} | |
impl MovZ { | |
const MASKED: u32 = 0b110100101_00_0000000000000000_00000; | |
pub fn encode(&self) -> u32 { | |
Self::MASKED | |
| (if self.imm16 > 0xFFFF { 1 } else { 0 } << 21) | |
| (if self.imm16 > 0xFFFF { | |
self.imm16 >> 16 | |
} else { | |
self.imm16 | |
} << 5) | |
| (self.rd as u32) | |
} | |
} | |
use skyline::{hooks::*, libc}; | |
extern "C" { | |
pub fn A64InlineHook(symbol: *const libc::c_void, replace: *const libc::c_void); | |
} | |
#[no_mangle] | |
pub extern "C" fn inline_hook_for_table_adrp_skyline_internal_original_fn(ctx: &mut InlineCtx) { | |
unsafe { | |
let id = *ctx.registers[18].x.as_ref() as u16; | |
let data = HOOK_DATA_FOR_ID.get(&id).unwrap(); | |
*ctx.registers[(*data).register as usize].x.as_mut() = (*data).ptr as u64; | |
} | |
} | |
pub fn patch_id_and_jump_at_offset(ptr: usize, register: u8, text_offset: usize) { | |
let target_ptr = { | |
if [0x23f0b40, 0x240bcb0, 0x240c00c].contains(&text_offset) { | |
ptr + 32 | |
} else { | |
ptr | |
} | |
}; | |
let data = Data { ptr: target_ptr, register }; | |
let movz = MovZ { | |
imm16: match get_id_if_data_exists(&data) { | |
Some(id) => id as u32, | |
None => insert_data(data) as u32, | |
}, | |
rd: 18, | |
}; | |
skyline::patching::Patch::in_text(text_offset) | |
.data(movz.encode()) | |
.unwrap(); | |
skyline::patching::Patch::in_text(text_offset + 4) | |
.nop() | |
.unwrap(); | |
unsafe { | |
println!( | |
"HOOK LOCATION: {:#x} - {:#x}", | |
text_offset, | |
getRegionAddress(Region::Text) as usize + text_offset | |
); | |
A64InlineHook( | |
(getRegionAddress(Region::Text) as usize + text_offset + 4) as _, | |
inline_hook_for_table_adrp_skyline_internal_original_fn as _, | |
); | |
} | |
} | |
pub fn patch_id_and_jump_xrefs(ptr: usize, xrefs: &Vec<PatchLocation>) { | |
for xref in xrefs { | |
patch_id_and_jump_at_offset(ptr, xref.adrp.rd, xref.text_offset); | |
} | |
} | |
fn get_text() -> &'static [u8] { | |
unsafe { | |
let ptr = getRegionAddress(Region::Text) as *const u8; | |
let size = (getRegionAddress(Region::Rodata) as usize) - (ptr as usize); | |
std::slice::from_raw_parts(ptr, size) | |
} | |
} | |
impl NarrationCharacallTable { | |
pub fn new(offset: usize, size: usize) -> NarrationCharacallTable { | |
// clone table with offset and size | |
unsafe { | |
let data = getRegionAddress(Region::Text) as usize; | |
// Subtract start of data offset | |
let mut current_offset = offset; | |
let mut table: Vec<NarrationCharacallEntry> = vec![]; | |
for _ in 0..size { | |
let narration_chara_call_entry = *((data + current_offset) | |
as *const NarrationCharacallEntry); | |
table.push(narration_chara_call_entry); | |
current_offset += | |
core::mem::size_of::<NarrationCharacallEntry>(); | |
} | |
let mut patch_locations: Vec<PatchLocation> = TableRefIter::new(&get_text(), 0x4545228) | |
.into_iter() | |
.collect(); | |
for x in &patch_locations { | |
println!("Location: {:#x} - {}", x.text_offset, x.adrp.rd); | |
} | |
NarrationCharacallTable { | |
offset: offset, | |
refs: patch_locations, | |
table: table, | |
} | |
} | |
} | |
pub fn push(&mut self, entry: NarrationCharacallEntry) { | |
self.table.push(entry); | |
} | |
pub fn patch(&mut self) { | |
let table_ptr = self.table.as_ptr() as usize; | |
patch_id_and_jump_xrefs(table_ptr, &self.refs); | |
println!("{:#x?}", self.table); | |
println!("{:#x}", table_ptr); | |
} | |
} | |
/* Everything here was written by jam1garner and can be found in the following repo: | |
* https://github.com/jam1garner/stage-table-refs | |
* | |
* Changed StageRef to TableRef for more generic naming | |
*/ | |
use std::iter::{Copied, Enumerate, StepBy}; | |
use std::slice::ArrayWindows; | |
pub struct TableRefIter<I: Iterator<Item = [u8; 8]>> { | |
pub table_offset: usize, | |
pub inner: Enumerate<I>, | |
} | |
const BOTTOM_12_BITS: usize = 0b1111_1111_1111; | |
pub struct PatchLocation { | |
pub text_offset: usize, | |
pub offset_from_table_start: usize, | |
pub adrp: Adrp, | |
pub add: AddImm, | |
} | |
impl<I: Iterator<Item = [u8; 8]>> Iterator for TableRefIter<I> { | |
type Item = PatchLocation; | |
fn next(&mut self) -> Option<Self::Item> { | |
while let Some((i, bytes)) = self.inner.next() { | |
let adrp = u32::from_le_bytes(bytes[..4].try_into().unwrap()); | |
let add = u32::from_le_bytes(bytes[4..].try_into().unwrap()); | |
match (Adrp::decode(adrp), AddImm::decode(add)) { | |
(Some(adrp), Some(add)) => { | |
if add.rd != add.rn || add.rd != adrp.rd { | |
continue; | |
} | |
let adrp_offset = adrp.imm; | |
let add_offset = add.imm12; | |
let instr_loc = i * 4; | |
let offset = (instr_loc & !BOTTOM_12_BITS) | |
+ (adrp_offset as usize) | |
+ (add_offset as usize); | |
if (self.table_offset..self.table_offset + 0x100).contains(&offset) { | |
return Some(PatchLocation { | |
add, | |
adrp, | |
text_offset: i * 4, | |
offset_from_table_start: offset - self.table_offset, | |
}); | |
} | |
} | |
_ => continue, | |
} | |
} | |
None | |
} | |
} | |
type TextIter8<'a> = Copied<StepBy<ArrayWindows<'a, u8, 8>>>; | |
impl<'a> TableRefIter<TextIter8<'a>> { | |
pub fn new(text: &'a [u8], table_offset: usize) -> Self { | |
Self { | |
table_offset, | |
inner: text.array_windows().step_by(4).copied().enumerate(), | |
} | |
} | |
} | |
pub struct CmpPatchLocation { | |
pub text_offset: usize, | |
pub instr: CmpImmediate, | |
} | |
type TextIter<'a> = Copied<StepBy<ArrayWindows<'a, u8, 4>>>; | |
pub struct TableCountRefIter<I: Iterator<Item = [u8; 4]>> { | |
pub inner: Enumerate<I>, | |
pub count_check: u16, | |
} | |
impl<'a> TableCountRefIter<TextIter<'a>> { | |
pub fn new(text: &'a [u8], count_check: u16) -> Self { | |
Self { | |
inner: text.array_windows().step_by(4).copied().enumerate(), | |
count_check, | |
} | |
} | |
} | |
impl<I: Iterator<Item = [u8; 4]>> Iterator for TableCountRefIter<I> { | |
type Item = CmpPatchLocation; | |
fn next(&mut self) -> Option<Self::Item> { | |
while let Some((i, bytes)) = self.inner.next() { | |
let instr = u32::from_le_bytes(bytes); | |
// if let Some(instr @ CmpImmediate { imm12: 0x76, .. }) = CmpImmediate::decode(instr) { | |
// return Some(CmpPatchLocation { instr, text_offset: i * 4}) | |
// } | |
match CmpImmediate::decode(instr) { | |
Some(instr) => { | |
if instr.imm12 == self.count_check { | |
return Some(CmpPatchLocation { | |
instr, | |
text_offset: i * 4, | |
}); | |
} | |
} | |
None => {} | |
} | |
} | |
None | |
} | |
} | |
pub struct TableRefs { | |
pub refs: Vec<CmpPatchLocation>, // References to locations | |
pub blacklisted_offsets: Vec<usize>, // A list of offsets not to patch | |
} | |
impl TableRefs { | |
pub fn new(count_to_check: u16, blacklisted_offsets: Vec<usize>) -> TableRefs { | |
TableRefs { | |
refs: TableCountRefIter::new(get_text(), count_to_check) | |
.into_iter() | |
.collect(), | |
blacklisted_offsets, | |
} | |
} | |
pub fn set_count(&mut self, count: u16) { | |
for xref in &self.refs { | |
let new_cmp: CmpImmediate = CmpImmediate { | |
imm12: count, | |
reg: xref.instr.reg, | |
is_64_bit: xref.instr.is_64_bit, | |
}; | |
// println!( | |
// ".text+{:#x?} - patch `{}` with `{}` {:#x}", | |
// xref.text_offset, | |
// xref.instr, | |
// new_cmp, | |
// new_cmp.encode() | |
// ); | |
// std::thread::sleep(std::time::Duration::from_millis(1000)); | |
// | |
if !self.blacklisted_offsets.contains(&xref.text_offset) { | |
skyline::patching::Patch::in_text(xref.text_offset) | |
.data(new_cmp.encode()) | |
.unwrap(); | |
} | |
} | |
} | |
} | |
pub fn install() { | |
let mut table = NarrationCharacallTable::new(0x4545228, 0x21); | |
table.push(NarrationCharacallEntry { | |
unk_1: 0x46, | |
nus3bank_path: 0x3b87269209, // same path as normal vc narration stuff with vc_narration_characall_silver instead | |
tonelabel_path: 0x3ccfbfc5ab, | |
nus3audio_path: 0x3cd12e503e, | |
unk_2: 0x1, | |
}); | |
table.patch(); | |
TableRefs::new(0x21, vec![]).set_count(table.table.len() as _); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Everything here (excluding the instructions not found in the original code) | |
* was written by jam1garner and can be found in the following repo: | |
* https://github.com/jam1garner/stage-table-refs | |
*/ | |
pub struct Adrp { | |
pub imm: u32, | |
pub rd: u8, | |
} | |
impl Adrp { | |
const MASK: u32 = 0b1_00_11111_0000000000000000000_00000; | |
const MASKED: u32 = 0b1_00_10000_0000000000000000000_00000; | |
pub fn decode(instr: u32) -> Option<Self> { | |
if instr & Self::MASK == Self::MASKED { | |
let rd = (instr & 0b11111) as u8; | |
let immhi = (instr >> 5) & 0x7FFFF; | |
let immlo = (instr >> 29) & 0b11; | |
let imm = (immhi << 14) + (immlo << 12); | |
Some(Self { imm, rd }) | |
} else { | |
None | |
} | |
} | |
pub fn encode(&self) -> u32 { | |
let immlo = (self.imm >> 12) & 0b11; | |
let immhi = self.imm >> 14; | |
Self::MASKED | (immlo << 29) | (immhi << 5) | (self.rd as u32) | |
} | |
} | |
pub struct AddImm { | |
pub imm12: u16, | |
pub shift: u8, | |
pub rn: u8, | |
pub rd: u8, | |
pub is_64_bit: bool, | |
} | |
impl AddImm { | |
const ADD_MASK: u32 = 0b01111111_00_000000000000_00000_00000; // 64-bit ADD immediate | |
const ADD_MASKED: u32 = 0b00010001_00_000000000000_00000_00000; | |
// 0b10010001_00_000000000001_00000_00000 | |
// 0b10010001_00_000000000001_00001_00000 | |
pub fn encode(&self) -> u32 { | |
let size = if self.is_64_bit { 1 } else { 0 }; | |
let imm12 = (self.imm12 as u32) << 10; | |
let rn = (self.rn as u32) << 5; | |
let rd = self.rd as u32; | |
Self::ADD_MASKED | (size << 31) | imm12 | rn | rd | |
} | |
pub fn decode(instr: u32) -> Option<Self> { | |
if instr & Self::ADD_MASK == Self::ADD_MASKED { | |
let rn = ((instr >> 5) & 0x1F) as u8; | |
let rd = (instr & 0x1F) as u8; | |
let imm12 = ((instr >> 10) & 0xFFF) as u16; | |
let shift = ((instr >> 22) & 0x3) as u8; | |
let is_64_bit = (instr >> 31) == 1; | |
Some(AddImm { | |
imm12, | |
shift, | |
rn, | |
rd, | |
is_64_bit, | |
}) | |
} else { | |
None | |
} | |
} | |
} | |
pub struct LdrImmediate { | |
pub imm9: u16, | |
pub rn: u8, | |
pub rt: u8, | |
pub is_64_bit: bool, | |
} | |
impl LdrImmediate { | |
const MASK: u32 = 0b10_111_1_11_11_1_000000000_11_00000_00000; | |
const MASKED: u32 = 0b10_111_0_00_01_0_000000000_01_00000_00000; | |
pub fn encode(&self) -> u32 { | |
let size = if self.is_64_bit { 1 } else { 0 }; | |
let imm9 = (self.imm9 as u32) << 12; | |
let rn = (self.rn as u32) << 5; | |
let rt = self.rt as u32; | |
Self::MASKED | size | imm9 | rn | rt | |
} | |
} | |
#[derive(Debug)] | |
pub struct CmpImmediate { | |
pub imm12: u16, | |
pub reg: u8, | |
pub is_64_bit: bool, | |
} | |
impl CmpImmediate { | |
const MASK: u32 = 0b011111111_0_000000000000_00000_11111; | |
const MASKED: u32 = 0b011100010_0_000000000000_00000_11111; | |
pub fn decode(instr: u32) -> Option<Self> { | |
if instr & Self::MASK == Self::MASKED { | |
let reg = ((instr >> 5) & 0b11111) as u8; | |
let imm12 = ((instr >> 10) & 0b111111111111) as u16; | |
let is_64_bit = (instr >> 31) == 1; | |
Some(Self { | |
reg, | |
imm12, | |
is_64_bit, | |
}) | |
} else { | |
None | |
} | |
} | |
pub fn encode(&self) -> u32 { | |
let sh = 0; | |
let sf = if self.is_64_bit { 1 } else { 0 }; | |
let imm12 = (self.imm12 & 0b111111111111) as u32; | |
let rn = (self.reg & 0b11111) as u32; | |
Self::MASKED | (imm12 << 10) | (rn << 5) | (sf << 31) | (sh << 22) | |
} | |
} | |
pub struct MovK { | |
pub imm16: u32, | |
pub rd: u8, | |
pub lsl_16: bool, | |
pub lsl_32: bool | |
} | |
impl MovK { | |
const MASKED: u32 = 0b111100101_00_0000000000000000_00000; | |
// 0b111100101_00_0010000110000001_00000 | |
// LSL #16 0b111100101_01_0010000110000001_00000 | |
// Normal mov 0b110100101_01_0000000010011100_00000 | |
// 0b111100101_10_0000000000000000_00000 | |
// 0b111100101_01_0000000000000000_00000 | |
// movk x8, #0x52, lsl #16 0b111100101_01_0000000001010010_01000 | |
// movk x8, #0x52, lsl #32 0b111100101_10_0000000001010010_01000 | |
pub fn encode(&self) -> u32 { | |
Self::MASKED | (if self.lsl_32 {1} else {0} << 22) | (if self.lsl_16 {1} else {0} << 21) | (self.imm16 << 5) | (self.rd as u32) | |
} | |
} | |
pub struct MovZ { | |
pub imm16: u32, | |
pub rd: u8, | |
} | |
impl MovZ { | |
const MASKED: u32 = 0b110100101_00_0000000000000000_00000; | |
pub fn encode(&self) -> u32 { | |
Self::MASKED | (if self.imm16 > 0xFFFF {1} else {0} << 21) | (if self.imm16 > 0xFFFF {self.imm16 >> 16} else {self.imm16} << 5) | (self.rd as u32) | |
} | |
} | |
pub struct LdrLiteralX { | |
pub imm19: u32, | |
pub rd: u8, | |
} | |
impl LdrLiteralX { | |
const MASKED: u32 = 0b01011000_0000000000000000000_00000; | |
pub fn encode(&self) -> u32 { | |
Self::MASKED | ({self.imm19} << 5) | (self.rd as u32) | |
} | |
} | |
pub struct BLR { | |
pub imm26: u64, | |
} | |
impl BLR { | |
const MASKED: u32 = 0b100101_000_00000000000000000000000; | |
// 0b100101_111_01110100011100011111000 | |
// 0b101111_111_01110100011100011111000 | |
pub fn encode(&self) -> u32 { | |
let overflow = self.imm26 > (u32::MAX as u64); | |
let imm16: u32 = if overflow { | |
(self.imm26 & 0xFFFFFF) as u32 | |
} else { | |
self.imm26 as u32 | |
}; | |
Self::MASKED | if overflow { 0b111 << 23 } else {0} | (imm16 >> 2) | |
} | |
} | |
pub struct BL { | |
pub imm26: u64, | |
} | |
impl BL { | |
const MASKED: u32 = 0b000101_000_00000000000000000000000; | |
pub fn encode(&self) -> u32 { | |
let overflow = self.imm26 > (u32::MAX as u64); | |
let imm16: u32 = if overflow { | |
(self.imm26 & 0xFFFFFF) as u32 | |
} else { | |
self.imm26 as u32 | |
}; | |
Self::MASKED | if overflow { 0b111 << 23 } else {0} | (imm16 >> 2) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment