Skip to content

Commit ea2ea5f

Browse files
committed
Push code for 1.22.1
1 parent 425db4b commit ea2ea5f

File tree

19 files changed

+811
-875
lines changed

19 files changed

+811
-875
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ gamedata = { path = "crates/gamedata" }
2020
loader = { path = "crates/loader" }
2121
mods = { path = "crates/mods" }
2222
unity = { version = "0.3.0", git = "https://github.com/DivineDragonFanClub/unity" }
23-
engage = { version = "0.9.0", git = "https://github.com/DivineDragonFanClub/engage" }
23+
engage = { version = "0.10.0", git = "https://github.com/DivineDragonFanClub/engage" }
2424
horizon-svc = { git = "https://github.com/skyline-rs/horizon-svc" } # We don't include the implementations because exlaunch already has them
2525
camino = "1.0.7"
2626
semver = { version = "1" }

crates/cobalt/Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
[package]
22
name = "cobalt"
3-
version = "1.22.0"
3+
version = "1.22.1"
44
edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77

88
[dependencies]
99
skyline = { git = "https://github.com/ultimate-research/skyline-rs" }
1010
unity = { version = "0.3.0", git = "https://github.com/DivineDragonFanClub/unity" }
11-
engage = { version = "0.9.0", git = "https://github.com/DivineDragonFanClub/engage" }
11+
engage = { version = "0.10.0", git = "https://github.com/DivineDragonFanClub/engage" }
1212
mods = { path = "../mods" }
1313
updater = { path = "../updater" }
1414
localize = { git = "https://github.com/DivineDragonFanClub/localize" }

crates/cobalt/src/audio.rs

+244
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
use std::sync::atomic::AtomicBool;
2+
3+
use unity::{
4+
prelude::*,
5+
system::Dictionary
6+
};
7+
8+
use engage::{
9+
gamedata::unit::Unit,
10+
gamesound::GameSound,
11+
combat::{
12+
Character,
13+
CharacterSound
14+
},
15+
};
16+
17+
use mods::manager::Manager;
18+
19+
use camino::Utf8PathBuf;
20+
21+
pub mod gamesound;
22+
pub mod soundmanager;
23+
pub mod soundplay;
24+
pub mod wwise;
25+
26+
pub const COBALT_EVENT_MARKER_PREFIX: &str = "CobaltEvent:";
27+
pub const ORIGINAL_SUFFIX: &str = "_Original";
28+
29+
pub static PLAY_ORIGINAL_V_PICK: AtomicBool = AtomicBool::new(false);
30+
pub static mut PREVIOUS_LAST_PICK_VOICE: u8 = 0;
31+
pub static mut UNSAFE_CHARACTER_PTR: *const Character = std::ptr::null();
32+
33+
fn get_event_or_fallback(event_name: &Il2CppString) -> &Il2CppString {
34+
match ParsedVoice::parse(event_name) {
35+
ParsedVoice::ModdedVoiceEvent(mod_voice) => {
36+
let mod_string = mod_voice.mod_event.to_string();
37+
let mod_str = mod_string.as_str();
38+
let fallback_string = mod_voice.fallback_event.to_string();
39+
let fallback_str = fallback_string.as_str();
40+
41+
match mod_str.rsplit_once('_') {
42+
Some((event_body_str, event_main_str)) => {
43+
let event_main = Il2CppString::new(format!("{}_{}", event_body_str, event_main_str));
44+
45+
if GameSound::is_event_loaded(event_main) {
46+
event_main
47+
} else {
48+
let event_fallback = Il2CppString::new(format!("{}_{}", event_body_str, fallback_str));
49+
event_fallback
50+
}
51+
},
52+
None => event_name,
53+
}
54+
},
55+
ParsedVoice::DefaultVoiceEvent(default_voice) => {
56+
println!("Getting Default voice: {}", default_voice.event);
57+
default_voice.event
58+
},
59+
}
60+
}
61+
62+
enum ParsedVoice<'a> {
63+
ModdedVoiceEvent(ModdedVoiceEvent<'a>),
64+
DefaultVoiceEvent(DefaultVoiceEvent<'a>),
65+
}
66+
67+
/// Represents a modded voice name with a fallback option.
68+
///
69+
/// For example, in `Voice="SeasideDragon!PlayerF"`, `SeasideDragon` is the modded voice name,
70+
/// and `PlayerF` is the fallback voice name that will be used if the modded voice is not available.
71+
struct ModdedVoiceEvent<'a> {
72+
/// The name of the modded voice to be used, e.g., `SeasideDragon`.
73+
mod_event: &'a Il2CppString,
74+
/// The fallback voice name, e.g., `PlayerF`, used if the modded voice is unavailable.
75+
/// This is not necessarily a voice name from the vanilla game - it could be another modded voice name (though this has not been tested).
76+
fallback_event: &'a Il2CppString,
77+
}
78+
79+
/// Represents an original voice name without any fallbacks.
80+
struct DefaultVoiceEvent<'a> {
81+
event: &'a Il2CppString,
82+
}
83+
84+
impl ParsedVoice<'_> {
85+
fn parse(event_name: &Il2CppString) -> ParsedVoice {
86+
let event_string = event_name.to_string();
87+
let parts: Vec<&str> = event_string.split('!').collect();
88+
89+
if parts.len() == 2 {
90+
return ParsedVoice::ModdedVoiceEvent(ModdedVoiceEvent {
91+
mod_event: Il2CppString::new(parts[0]),
92+
fallback_event: Il2CppString::new(parts[1]),
93+
});
94+
} else {
95+
return ParsedVoice::DefaultVoiceEvent(DefaultVoiceEvent { event: event_name });
96+
}
97+
}
98+
}
99+
100+
fn get_switchname_fallback(switch_name: &Il2CppString) -> &Il2CppString {
101+
match switch_name.to_string().split('!').nth(1) {
102+
Some(switch_fallback) => Il2CppString::new(switch_fallback),
103+
None => switch_name,
104+
}
105+
}
106+
107+
#[skyline::hook(offset = 0x1F87570)]
108+
pub fn unitinfo_reservecharavoice(
109+
side: i32,
110+
person_switch_name: &Il2CppString,
111+
engage_switch_name: Option<&Il2CppString>,
112+
event_name: &Il2CppString,
113+
method_info: OptionalMethod,
114+
) {
115+
let event_string = event_name.to_string();
116+
let person_string = person_switch_name.to_string();
117+
118+
// println!("[UnitInfo] Event name: {}", event_string);
119+
// println!("[UnitInfo] Person Switch name: {}", person_string);
120+
121+
match event_string.as_str() {
122+
"V_Engage_Respond" => {
123+
let modded_event = Il2CppString::new(format!("{}_{}", event_string, person_string));
124+
125+
match GameSound::is_event_loaded(modded_event) {
126+
true => call_original!(side, person_switch_name, engage_switch_name, modded_event, method_info),
127+
false => call_original!(side, person_switch_name, engage_switch_name, event_name, method_info),
128+
}
129+
},
130+
131+
_ => call_original!(side, person_switch_name, engage_switch_name, event_name, method_info),
132+
}
133+
}
134+
135+
// AkMarkerCallbackInfo$$get_strLabel 7102f26bd0 System_String_o * AkMarkerCallbackInfo$$get_strLabel(AkMarkerCallbackInfo_o * __this, MethodInfo * method) 172
136+
#[unity::from_offset("", "AkMarkerCallbackInfo", "get_strLabel")]
137+
fn akmarkercallbackinfo_get_strlabel(this: &(), method_info: OptionalMethod) -> &'static Il2CppString;
138+
139+
// Combat.Character$$get_Sound 7102b00c40 Combat_CharacterSound_o * Combat.Character$$get_Sound(Combat_Character_o * __this, MethodInfo * method) 140
140+
#[unity::from_offset("Combat", "Character", "get_Sound")]
141+
fn combat_character_get_sound(this: &Character, method_info: OptionalMethod) -> &'static CharacterSound;
142+
143+
// Combat.CharacterSound$$PlayVoice 71025f0be0 void Combat.CharacterSound$$PlayVoice(Combat_CharacterSound_o * __this, System_String_o * eventName, MethodInfo * method) 556
144+
#[unity::from_offset("Combat", "CharacterSound", "PlayVoice")]
145+
fn combat_charactersound_play_voice(this: &CharacterSound, event_name: &Il2CppString, method_info: OptionalMethod);
146+
147+
#[unity::from_offset("App", "FileCommon", "GetFullPath")]
148+
fn filecommon_getfullpath(path: &Il2CppString, method_info: OptionalMethod) -> &'static mut Il2CppString;
149+
150+
#[unity::class("App", "FileCommon")]
151+
pub struct FileCommon {}
152+
153+
#[repr(C)]
154+
pub struct FileCommonStaticFields<'a> {
155+
lock_object: &'a (),
156+
dictionary: &'a Dictionary<&'a Il2CppString, &'a mut FileData>,
157+
// Meh
158+
}
159+
160+
#[unity::class("App", "FileHandle")]
161+
pub struct FileHandle {
162+
data: &'static mut FileData,
163+
}
164+
165+
impl FileHandle {
166+
pub fn unload(&self) {
167+
let method = self.get_class().get_method_from_name("Unload", 0).unwrap();
168+
169+
let unload = unsafe { std::mem::transmute::<_, extern "C" fn(&Self, &MethodInfo)>(method.method_ptr) };
170+
171+
unload(&self, method);
172+
}
173+
}
174+
175+
#[unity::class("App", "FileData")]
176+
pub struct FileData {
177+
state: i32,
178+
path: &'static Il2CppString,
179+
data: &'static mut Il2CppArray<u8>,
180+
refer: &'static mut BindHolder,
181+
}
182+
183+
#[unity::class("App", "BindHolder")]
184+
pub struct BindHolder {
185+
bind: i32,
186+
}
187+
188+
#[unity::from_offset("App", "BindHolder", "Bind")]
189+
fn bindholder_bind(this: &mut BindHolder, method_info: OptionalMethod) -> bool;
190+
191+
#[skyline::hook(offset = 0x328fff0)]
192+
fn filehandle_loadasync(this: &'static mut FileHandle, path: &Il2CppString, method_info: OptionalMethod) {
193+
// println!("[FileHandle::LoadAsync] Path: {}", path.to_string());
194+
195+
let mod_path = Utf8PathBuf::from("Data/StreamingAssets/").join(path.to_string());
196+
197+
// Check if we have that voiceline file in our mods and load it ourselves if we do
198+
if Manager::get().exists(&mod_path) {
199+
// Resets FileHandle
200+
this.unload();
201+
202+
let full_path = unsafe { filecommon_getfullpath(path, None) };
203+
204+
let static_fields = FileCommon::class().get_static_fields_mut::<FileCommonStaticFields>();
205+
206+
// Check if there's already a cached version of the voiceline
207+
if static_fields.dictionary.try_get_value(full_path, &mut this.data) == false {
208+
let mut file = Manager::get().get_file(mod_path).unwrap();
209+
210+
// Initialize the FileData pointer
211+
this.data = FileData::instantiate().unwrap();
212+
this.data.path = full_path;
213+
this.data.data = Il2CppArray::<u8>::from_slice(&mut file).unwrap();
214+
this.data.state = 2;
215+
this.data.refer = BindHolder::instantiate().unwrap();
216+
217+
// Add our fully loaded file to the cache list
218+
static_fields.dictionary.add(full_path, this.data);
219+
}
220+
221+
unsafe {
222+
bindholder_bind(this.data.refer, None);
223+
}
224+
} else {
225+
call_original!(this, path, method_info)
226+
}
227+
}
228+
229+
// Combat.CharacterAppearance$$CreateForSound 7102b0f340 Combat_CharacterAppearance_o * Combat.CharacterAppearance$$CreateForSound(App_Unit_o * unit, MethodInfo * method) 280
230+
#[unity::from_offset("Combat", "CharacterAppearance", "CreateForSound")]
231+
pub fn combat_character_appearance_create_for_sound(unit: &Unit, method_info: OptionalMethod) -> &'static CharacterAppearance;
232+
233+
#[unity::class("App", "AssetTable.Sound")]
234+
pub struct AssetTableSound {
235+
voice_id: &'static Il2CppString,
236+
footstep_id: &'static Il2CppString,
237+
material_id: &'static Il2CppString,
238+
}
239+
240+
#[unity::class("Combat", "CharacterAppearance")]
241+
pub struct CharacterAppearance {
242+
padding: [u8; 0x88],
243+
sound: AssetTableSound,
244+
}

0 commit comments

Comments
 (0)