il2cpp_bridge_rs/api/debugging/
cs.rs1use crate::api::{cache, Application};
3#[cfg(dev_release)]
4use crate::logger;
5use crate::structs::Assembly;
6use std::fs::File;
7use std::io::{BufWriter, Write};
8use std::path::{Path, PathBuf};
9
10fn write_assembly(writer: &mut dyn Write, assembly: &Assembly) -> std::io::Result<()> {
12 if !assembly.classes.is_empty() {
13 for class in &assembly.classes {
14 writeln!(writer, "\n{}", class)?;
15 }
16 }
17 Ok(())
18}
19
20fn sorted_assemblies() -> Vec<std::sync::Arc<Assembly>> {
23 let mut assemblies: Vec<_> = cache::CACHE
24 .assemblies
25 .iter()
26 .map(|e| std::sync::Arc::clone(e.value()))
27 .collect();
28
29 assemblies.sort_by_key(|a| {
30 a.classes
31 .iter()
32 .map(|c| c.token & 0x00FF_FFFF)
33 .min()
34 .unwrap_or(u32::MAX)
35 });
36
37 assemblies
38}
39
40fn write_image_list(
43 writer: &mut dyn Write,
44 assemblies: &[std::sync::Arc<Assembly>],
45) -> std::io::Result<()> {
46 for (idx, assembly) in assemblies.iter().enumerate() {
47 let start = assembly
48 .classes
49 .iter()
50 .map(|c| c.token & 0x00FF_FFFF)
51 .min()
52 .map(|t| t.saturating_sub(1))
53 .unwrap_or(0);
54
55 writeln!(writer, "// Image {}: {} - {}", idx, assembly.file, start)?;
56 }
57 writeln!(writer)
58}
59
60fn get_dump_dir(base_path: Option<&str>) -> Option<PathBuf> {
62 let root = if let Some(path) = base_path {
63 path.to_string()
64 } else {
65 Application::data_path().unwrap_or(".".to_string())
66 };
67
68 let dump_dir = Path::new(&root).join("dump");
69
70 if let Err(_e) = std::fs::create_dir_all(&dump_dir) {
71 #[cfg(dev_release)]
72 logger::error(&format!("Failed to create dump directory: {}", _e));
73 return None;
74 }
75
76 Some(dump_dir)
77}
78
79fn dump_assemblies_impl(base_path: Option<&str>, single_file_name: Option<&str>) -> Option<String> {
81 let dump_dir = get_dump_dir(base_path)?;
82
83 #[cfg(dev_release)]
84 logger::info(&format!("Dumping assemblies to {:?}...", dump_dir));
85
86 let assemblies = sorted_assemblies();
87
88 if let Some(file_name) = single_file_name {
89 let path = dump_dir.join(file_name);
91 let file = match File::create(&path) {
92 Ok(f) => f,
93 Err(_e) => {
94 #[cfg(dev_release)]
95 logger::error(&format!("Failed to create dump file: {}", _e));
96 return None;
97 }
98 };
99 let mut writer = BufWriter::new(file);
100
101 if let Err(_e) = write_image_list(&mut writer, &assemblies) {
103 #[cfg(dev_release)]
104 logger::error(&format!("Failed to write image list: {}", _e));
105 }
106
107 for assembly in &assemblies {
109 if let Err(_e) = write_assembly(&mut writer, assembly) {
110 #[cfg(dev_release)]
111 logger::error(&format!(
112 "Failed to write assembly {}: {}",
113 assembly.name, _e
114 ));
115 }
116 }
117
118 if let Err(_e) = writer.flush() {
119 #[cfg(dev_release)]
120 logger::error(&format!("Failed to flush writer: {}", _e));
121 return None;
122 }
123
124 #[cfg(dev_release)]
125 logger::info(&format!("Dumped all assemblies to {:?}", path));
126 Some(path.to_string_lossy().into_owned())
127 } else {
128 for assembly in &assemblies {
130 let path = dump_dir.join(format!("{}.cs", assembly.name));
131
132 let file = match File::create(&path) {
133 Ok(f) => f,
134 Err(_e) => {
135 #[cfg(dev_release)]
136 logger::error(&format!(
137 "Failed to create dump file for {}: {}",
138 assembly.name, _e
139 ));
140 continue;
141 }
142 };
143 let mut writer = BufWriter::new(file);
144
145 if let Err(_e) = write_image_list(&mut writer, &assemblies) {
146 #[cfg(dev_release)]
147 logger::error(&format!("Failed to write image list: {}", _e));
148 }
149
150 if let Err(_e) = write_assembly(&mut writer, assembly) {
151 #[cfg(dev_release)]
152 logger::error(&format!(
153 "Failed to write assembly {}: {}",
154 assembly.name, _e
155 ));
156 }
157
158 if writer.flush().is_ok() {
159 #[cfg(dev_release)]
160 logger::info(&format!("Successfully dumped assembly {}", assembly.name));
161 }
162 }
163
164 #[cfg(dev_release)]
165 logger::info("Dumped all assemblies");
166 Some(dump_dir.to_string_lossy().into_owned())
167 }
168}
169
170pub fn dump_assembly(assembly_to_dump: Option<&str>) -> Option<()> {
175 if assembly_to_dump.is_none() {
177 return dump_all().map(|_| ());
178 }
179
180 let target_name = assembly_to_dump.unwrap();
181
182 let dump_dir = get_dump_dir(None)?;
183 let file_name = format!("{}.cs", target_name);
184 let path = dump_dir.join(file_name);
185
186 let file = match File::create(&path) {
187 Ok(f) => f,
188 Err(_e) => {
189 #[cfg(dev_release)]
190 logger::error(&format!("Failed to create dump file: {}", _e));
191 return None;
192 }
193 };
194 let mut writer = BufWriter::new(file);
195
196 #[cfg(dev_release)]
197 logger::info(&format!("Dumping assembly {}", target_name));
198
199 let assemblies = sorted_assemblies();
200
201 if let Err(_e) = write_image_list(&mut writer, &assemblies) {
203 #[cfg(dev_release)]
204 logger::error(&format!("Failed to write image list: {}", _e));
205 }
206
207 for assembly in &assemblies {
208 if assembly.name.contains(target_name) {
209 if let Err(_e) = write_assembly(&mut writer, assembly) {
210 #[cfg(dev_release)]
211 logger::error(&format!("Failed to write assembly header: {}", _e));
212 return None;
213 }
214 }
215 }
216
217 if let Err(_e) = writer.flush() {
218 #[cfg(dev_release)]
219 logger::error(&format!("Failed to flush writer: {}", _e));
220 return None;
221 }
222
223 #[cfg(dev_release)]
224 logger::info(&format!("Dumped assembly to {:?}", path));
225 Some(())
226}
227
228pub fn dump() -> Option<String> {
232 dump_assemblies_impl(None, None)
233}
234
235pub fn dump_all() -> Option<String> {
239 dump_assemblies_impl(None, Some("dump.cs"))
240}
241
242pub fn dump_to(base_path: &str) -> Option<String> {
246 dump_assemblies_impl(Some(base_path), None)
247}
248
249pub fn dump_all_to(base_path: &str) -> Option<String> {
253 dump_assemblies_impl(Some(base_path), Some("dump.cs"))
254}