1use std::{
5 fmt::Write,
6 hash::{Hash, Hasher},
7 path::{Path, PathBuf},
8};
9
10#[cfg(feature = "python_bindings")]
11use pyo3::prelude::*;
12
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16use crate::{symbol, utils, SymbolDecompStateIter};
17
18#[derive(Debug, Clone)]
19#[non_exhaustive]
20#[cfg_attr(feature = "python_bindings", pyclass(module = "mapfile_parser"))]
21#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22pub struct Section {
23 pub filepath: PathBuf,
24
25 pub vram: u64,
26
27 pub size: u64,
28
29 pub section_type: String,
30
31 pub vrom: Option<u64>,
32
33 pub align: Option<u64>,
34
35 pub is_fill: bool,
36
37 pub symbols: Vec<symbol::Symbol>,
38}
39
40impl Section {
41 pub fn new(
42 filepath: PathBuf,
43 vram: u64,
44 size: u64,
45 section_type: &str,
46 vrom: Option<u64>,
47 align: Option<u64>,
48 ) -> Self {
49 Self::new_impl(filepath, vram, size, section_type, vrom, align, false)
50 }
51
52 pub(crate) fn new_impl(
53 filepath: PathBuf,
54 vram: u64,
55 size: u64,
56 section_type: &str,
57 vrom: Option<u64>,
58 align: Option<u64>,
59 is_fill: bool,
60 ) -> Self {
61 Self {
62 filepath,
63 vram,
64 size,
65 section_type: section_type.into(),
66 vrom,
67 align,
68 is_fill,
69 symbols: Vec::new(),
70 }
71 }
72
73 pub fn is_noload_section(&self) -> bool {
74 utils::is_noload_section(&self.section_type)
75 }
76
77 pub fn find_symbol_by_name(&self, sym_name: &str) -> Option<&symbol::Symbol> {
78 self.symbols.iter().find(|&sym| sym.name == sym_name)
79 }
80
81 pub fn find_symbol_and_index_by_name(
82 &self,
83 sym_name: &str,
84 ) -> Option<(&symbol::Symbol, usize)> {
85 for (index, sym) in self.symbols.iter().enumerate() {
86 if sym.name == sym_name {
87 return Some((sym, index));
88 }
89 }
90 None
91 }
92
93 pub fn find_symbol_by_name_mut(&mut self, sym_name: &str) -> Option<&mut symbol::Symbol> {
94 self.symbols.iter_mut().find(|sym| sym.name == sym_name)
95 }
96
97 #[deprecated(
98 since = "2.7.0",
99 note = "Use `find_symbol_by_vram` or `find_symbol_by_vrom` instead."
100 )]
101 pub fn find_symbol_by_vram_or_vrom(&self, address: u64) -> Option<(&symbol::Symbol, i64)> {
102 let mut prev_sym: Option<&symbol::Symbol> = None;
103
104 let is_vram = address >= 0x1000000;
105
106 for sym in &self.symbols {
107 if sym.vram == address {
108 return Some((sym, 0));
109 }
110 if let Some(sym_vrom_temp) = sym.vrom {
111 if sym_vrom_temp == address {
112 return Some((sym, 0));
113 }
114 }
115
116 if let Some(prev_sym_temp) = prev_sym {
117 if let Some(sym_vrom) = sym.vrom {
118 if sym_vrom > address {
119 if let Some(prev_vrom_temp) = prev_sym_temp.vrom {
120 let offset = address as i64 - prev_vrom_temp as i64;
121 if offset < 0 {
122 return None;
123 }
124 return Some((prev_sym_temp, offset));
125 }
126 }
127 }
128 if is_vram && sym.vram > address {
129 let offset = address as i64 - prev_sym_temp.vram as i64;
130 if offset < 0 {
131 return None;
132 }
133 return Some((prev_sym_temp, offset));
134 }
135 }
136
137 prev_sym = Some(sym);
138 }
139
140 if let Some(prev_sym_temp) = prev_sym {
141 if let Some(prev_sym_temp_vrom) = prev_sym_temp.vrom {
142 if prev_sym_temp_vrom + prev_sym_temp.size > address {
143 let offset = address as i64 - prev_sym_temp_vrom as i64;
144 if offset < 0 {
145 return None;
146 }
147 return Some((prev_sym_temp, offset));
148 }
149 }
150
151 if is_vram && prev_sym_temp.vram + prev_sym_temp.size > address {
152 let offset = address as i64 - prev_sym_temp.vram as i64;
153 if offset < 0 {
154 return None;
155 }
156 return Some((prev_sym_temp, offset));
157 }
158 }
159
160 None
161 }
162
163 pub fn find_symbol_by_vram(&self, address: u64) -> Option<(&symbol::Symbol, i64)> {
164 let mut prev_sym: Option<&symbol::Symbol> = None;
165
166 for sym in &self.symbols {
167 if sym.vram == address {
168 return Some((sym, 0));
169 }
170
171 if let Some(prev_sym_temp) = prev_sym {
172 if sym.vram > address {
173 let offset = address as i64 - prev_sym_temp.vram as i64;
174 if offset < 0 {
175 return None;
176 }
177 return Some((prev_sym_temp, offset));
178 }
179 }
180
181 prev_sym = Some(sym);
182 }
183
184 if let Some(prev_sym_temp) = prev_sym {
185 if prev_sym_temp.vram + prev_sym_temp.size > address {
186 let offset = address as i64 - prev_sym_temp.vram as i64;
187 if offset < 0 {
188 return None;
189 }
190 return Some((prev_sym_temp, offset));
191 }
192 }
193
194 None
195 }
196
197 pub fn find_symbol_by_vrom(&self, address: u64) -> Option<(&symbol::Symbol, i64)> {
198 let mut prev_sym: Option<&symbol::Symbol> = None;
199
200 for sym in &self.symbols {
201 if let Some(sym_vrom_temp) = sym.vrom {
202 if sym_vrom_temp == address {
203 return Some((sym, 0));
204 }
205 }
206
207 if let Some(prev_sym_temp) = prev_sym {
208 if let Some(sym_vrom) = sym.vrom {
209 if sym_vrom > address {
210 if let Some(prev_vrom_temp) = prev_sym_temp.vrom {
211 let offset = address as i64 - prev_vrom_temp as i64;
212 if offset < 0 {
213 return None;
214 }
215 return Some((prev_sym_temp, offset));
216 }
217 }
218 }
219 }
220
221 prev_sym = Some(sym);
222 }
223
224 if let Some(prev_sym_temp) = prev_sym {
225 if let Some(prev_sym_temp_vrom) = prev_sym_temp.vrom {
226 if prev_sym_temp_vrom + prev_sym_temp.size > address {
227 let offset = address as i64 - prev_sym_temp_vrom as i64;
228 if offset < 0 {
229 return None;
230 }
231 return Some((prev_sym_temp, offset));
232 }
233 }
234 }
235
236 None
237 }
238
239 #[deprecated(
240 since = "2.8.0",
241 note = "This functionality is perform automatically during parsing now."
242 )]
243 pub fn fixup_non_matching_symbols(&mut self) {
244 }
246
247 pub fn to_csv_header(print_vram: bool) -> String {
248 let mut ret = String::new();
249
250 if print_vram {
251 ret.push_str("VRAM,");
252 }
253 ret.push_str("File,Section type,Num symbols,Max size,Total size,Average size");
254 ret
255 }
256
257 pub fn to_csv(&self, print_vram: bool) -> String {
258 let mut ret = String::new();
259
260 let sym_count = self.symbols.len() as u64;
262 let mut max_size = 0;
263 let average_size = if sym_count > 0 {
264 self.size as f64 / sym_count as f64
265 } else {
266 self.size as f64 / 1.0
267 };
268
269 for sym in &self.symbols {
270 if sym.size > max_size {
271 max_size = sym.size;
272 }
273 }
274
275 if print_vram {
276 write!(ret, "{:08X},", self.vram).unwrap();
278 }
280 write!(
281 ret,
282 "{},{},{},{},{},{:0.2}",
283 self.filepath.display(),
284 self.section_type,
285 sym_count,
286 max_size,
287 self.size,
288 average_size
289 )
290 .unwrap();
291
292 ret
293 }
294
295 pub fn print_csv_header(print_vram: bool) {
296 println!("{}", Self::to_csv_header(print_vram));
297 }
298
299 pub fn print_as_csv(&self, print_vram: bool) {
300 println!("{}", self.to_csv(print_vram));
301 }
302}
303
304impl Section {
305 pub(crate) fn new_default(
306 filepath: std::path::PathBuf,
307 vram: u64,
308 size: u64,
309 section_type: &str,
310 ) -> Self {
311 Section {
312 filepath,
313 vram,
314 size,
315 section_type: section_type.into(),
316 vrom: None,
317 align: None,
318 is_fill: false,
319 symbols: Vec::new(),
320 }
321 }
322
323 pub(crate) fn new_placeholder() -> Self {
324 Self {
325 filepath: "".into(),
326 vram: 0,
327 size: 0,
328 section_type: "".into(),
329 vrom: None,
330 align: None,
331 is_fill: false,
332 symbols: Vec::new(),
333 }
334 }
335
336 pub(crate) fn new_fill(
337 filepath: std::path::PathBuf,
338 vram: u64,
339 size: u64,
340 section_type: &str,
341 ) -> Self {
342 Self {
343 filepath,
344 vram,
345 size,
346 section_type: section_type.into(),
347 vrom: None,
348 align: None,
349 is_fill: true,
350 symbols: Vec::new(),
351 }
352 }
353
354 pub fn is_placeholder(&self) -> bool {
355 self.filepath.as_os_str().is_empty()
356 && self.vram == 0
357 && self.size == 0
358 && self.section_type.is_empty()
359 && self.vrom.is_none()
360 && self.align.is_none()
361 && self.symbols.is_empty()
362 }
363
364 pub fn symbol_match_state_iter(
365 &self,
366 path_decomp_settings: Option<&PathDecompSettings>,
367 ) -> SymbolDecompStateIter {
368 let mut whole_file_is_undecomped = false;
369 let mut functions_path = None;
370
371 if let Some(path_decomp_settings) = path_decomp_settings {
372 let original_file_path: PathBuf = self
373 .filepath
374 .components()
375 .skip(path_decomp_settings.path_index)
376 .collect();
377
378 let mut extensionless_file_path = original_file_path;
379 while extensionless_file_path.extension().is_some() {
380 extensionless_file_path.set_extension("");
381 }
382
383 let full_asm_file = path_decomp_settings
384 .asm_path
385 .join(extensionless_file_path.with_extension("s"));
386 whole_file_is_undecomped = full_asm_file.exists();
387 functions_path = path_decomp_settings
388 .nonmatchings
389 .map(|x| x.join(extensionless_file_path.clone()));
390 }
391
392 SymbolDecompStateIter::new(self, whole_file_is_undecomped, functions_path)
393 }
394}
395
396pub struct PathDecompSettings<'ap, 'np> {
397 pub asm_path: &'ap Path,
398 pub path_index: usize,
399 pub nonmatchings: Option<&'np Path>,
400}
401
402impl PartialEq for Section {
404 fn eq(&self, other: &Self) -> bool {
405 self.filepath == other.filepath && self.section_type == other.section_type
406 }
407}
408impl Eq for Section {}
409
410impl Hash for Section {
412 fn hash<H: Hasher>(&self, state: &mut H) {
413 self.filepath.hash(state);
414 self.section_type.hash(state);
415 }
416}
417
418#[cfg(feature = "python_bindings")]
419#[allow(non_snake_case)]
420pub(crate) mod python_bindings {
421 use pyo3::{intern, prelude::*, IntoPyObjectExt};
422
423 use std::path::PathBuf;
424
425 use std::hash::{Hash, Hasher};
427
428 use crate::symbol;
429
430 use std::collections::hash_map::DefaultHasher;
431
432 use super::*;
433
434 #[pymethods]
435 impl Section {
436 #[new]
437 #[pyo3(signature = (filepath, vram, size, section_type, vrom=None, align=None, is_fill=false))]
438 fn py_new(
439 filepath: PathBuf,
440 vram: u64,
441 size: u64,
442 section_type: &str,
443 vrom: Option<u64>,
444 align: Option<u64>,
445 is_fill: bool,
446 ) -> Self {
447 Self::new_impl(filepath, vram, size, section_type, vrom, align, is_fill)
448 }
449
450 #[getter]
454 fn get_filepath(&self) -> PyResult<PyObject> {
455 Python::with_gil(|py| {
456 let pathlib = py.import("pathlib")?;
457 let pathlib_path = pathlib.getattr(intern!(py, "Path"))?;
458 let args = (self.filepath.clone(),);
459
460 pathlib_path.call1(args)?.into_py_any(py)
461 })
462 }
463
464 #[setter]
465 fn set_filepath(&mut self, value: PathBuf) -> PyResult<()> {
466 self.filepath = value;
467 Ok(())
468 }
469
470 #[getter]
471 fn get_vram(&self) -> PyResult<u64> {
472 Ok(self.vram)
473 }
474
475 #[setter]
476 fn set_vram(&mut self, value: u64) -> PyResult<()> {
477 self.vram = value;
478 Ok(())
479 }
480
481 #[getter]
482 fn get_size(&self) -> PyResult<u64> {
483 Ok(self.size)
484 }
485
486 #[setter]
487 fn set_size(&mut self, value: u64) -> PyResult<()> {
488 self.size = value;
489 Ok(())
490 }
491
492 #[getter]
493 fn get_sectionType(&self) -> PyResult<String> {
494 Ok(self.section_type.clone())
495 }
496
497 #[setter]
498 fn set_sectionType(&mut self, value: String) -> PyResult<()> {
499 self.section_type = value;
500 Ok(())
501 }
502
503 #[getter]
504 fn get_vrom(&self) -> PyResult<Option<u64>> {
505 Ok(self.vrom)
506 }
507
508 #[setter]
509 fn set_vrom(&mut self, value: Option<u64>) -> PyResult<()> {
510 self.vrom = value;
511 Ok(())
512 }
513
514 #[getter]
515 fn get_align(&self) -> PyResult<Option<u64>> {
516 Ok(self.align)
517 }
518
519 #[setter]
520 fn set_align(&mut self, value: Option<u64>) -> PyResult<()> {
521 self.align = value;
522 Ok(())
523 }
524
525 #[getter]
526 fn get_isFill(&self) -> PyResult<bool> {
527 Ok(self.is_fill)
528 }
529
530 #[setter]
531 fn set_isFill(&mut self, value: bool) -> PyResult<()> {
532 self.is_fill = value;
533 Ok(())
534 }
535
536 #[getter]
550 fn isNoloadSection(&self) -> bool {
551 self.is_noload_section()
552 }
553
554 fn getName(&self) -> PathBuf {
558 self.filepath
559 .with_extension("")
560 .components()
561 .skip(2)
562 .collect()
563 }
564
565 fn findSymbolByName(&self, sym_name: &str) -> Option<symbol::Symbol> {
566 self.find_symbol_by_name(sym_name).cloned()
567 }
568
569 fn findSymbolByVramOrVrom(&self, address: u64) -> Option<(symbol::Symbol, i64)> {
570 #[allow(deprecated)]
571 if let Some((sym, offset)) = self.find_symbol_by_vram_or_vrom(address) {
572 Some((sym.clone(), offset))
573 } else {
574 None
575 }
576 }
577
578 fn findSymbolByVram(&self, address: u64) -> Option<(symbol::Symbol, i64)> {
579 if let Some((sym, offset)) = self.find_symbol_by_vram(address) {
580 Some((sym.clone(), offset))
581 } else {
582 None
583 }
584 }
585
586 fn findSymbolByVrom(&self, address: u64) -> Option<(symbol::Symbol, i64)> {
587 if let Some((sym, offset)) = self.find_symbol_by_vrom(address) {
588 Some((sym.clone(), offset))
589 } else {
590 None
591 }
592 }
593
594 fn fixupNonMatchingSymbols(&mut self) {
595 #[allow(deprecated)]
596 self.fixup_non_matching_symbols()
597 }
598
599 #[staticmethod]
600 #[pyo3(signature=(print_vram=true))]
601 fn toCsvHeader(print_vram: bool) -> String {
602 Self::to_csv_header(print_vram)
603 }
604
605 #[pyo3(signature=(print_vram=true))]
606 fn toCsv(&self, print_vram: bool) -> String {
607 self.to_csv(print_vram)
608 }
609
610 #[staticmethod]
611 #[pyo3(signature=(print_vram=true))]
612 fn printCsvHeader(print_vram: bool) {
613 Self::print_csv_header(print_vram)
614 }
615
616 #[pyo3(signature=(print_vram=true))]
617 fn printAsCsv(&self, print_vram: bool) {
618 self.print_as_csv(print_vram)
619 }
620
621 fn copySymbolList(&self) -> Vec<symbol::Symbol> {
640 self.symbols.clone()
641 }
642
643 fn setSymbolList(&mut self, new_list: Vec<symbol::Symbol>) {
644 self.symbols = new_list;
645 }
646
647 fn appendSymbol(&mut self, sym: symbol::Symbol) {
648 self.symbols.push(sym);
649 }
650
651 fn __iter__(slf: PyRef<'_, Self>) -> PyResult<Py<SymbolVecIter>> {
652 let iter = SymbolVecIter {
653 inner: slf.symbols.clone().into_iter(),
654 };
655 Py::new(slf.py(), iter)
656 }
657
658 fn __getitem__(&self, index: usize) -> symbol::Symbol {
659 self.symbols[index].clone()
660 }
661
662 fn __setitem__(&mut self, index: usize, element: symbol::Symbol) {
663 self.symbols[index] = element;
664 }
665
666 fn __len__(&self) -> usize {
667 self.symbols.len()
668 }
669
670 fn __eq__(&self, other: &Self) -> bool {
671 self == other
672 }
673
674 fn __hash__(&self) -> isize {
675 let mut hasher = DefaultHasher::new();
676 self.hash(&mut hasher);
677 hasher.finish() as isize
678 }
679
680 }
682
683 #[pyclass]
684 struct SymbolVecIter {
685 inner: std::vec::IntoIter<symbol::Symbol>,
686 }
687
688 #[pymethods]
689 impl SymbolVecIter {
690 fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
691 slf
692 }
693
694 fn __next__(mut slf: PyRefMut<'_, Self>) -> Option<symbol::Symbol> {
695 slf.inner.next()
696 }
697 }
698}