ds_decomp/config/
relocations.rs

1use std::{
2    backtrace::Backtrace,
3    collections::{BTreeMap, btree_map},
4    fmt::Display,
5    io::{self, BufRead, BufReader, BufWriter, Write},
6    iter,
7    num::ParseIntError,
8    ops::Range,
9    path::Path,
10};
11
12use ds_rom::rom::raw::AutoloadKind;
13use serde::{Deserialize, Serialize};
14use snafu::Snafu;
15
16use crate::{
17    config::symbol::{Symbol, SymbolMap},
18    util::{
19        io::{FileError, create_file, open_file},
20        parse::{parse_i32, parse_u16, parse_u32},
21    },
22};
23
24use super::{
25    ParseContext, iter_attributes,
26    module::{Module, ModuleKind},
27};
28
29pub struct Relocations {
30    relocations: BTreeMap<u32, Relocation>,
31}
32
33#[derive(Debug, Snafu)]
34pub enum RelocationsParseError {
35    #[snafu(transparent)]
36    File { source: FileError },
37    #[snafu(transparent)]
38    Io { source: io::Error },
39    #[snafu(transparent)]
40    RelocationParse { source: RelocationParseError },
41}
42
43#[derive(Debug, Snafu)]
44pub enum RelocationsWriteError {
45    #[snafu(transparent)]
46    File { source: FileError },
47    #[snafu(transparent)]
48    Io { source: io::Error },
49}
50
51#[derive(Debug, Snafu)]
52pub enum RelocationsError {
53    #[snafu(display(
54        "Relocation from {from:#010x} to {curr_to:#010x} in {curr_module} collides with existing one to {prev_to:#010x} in {prev_module}"
55    ))]
56    RelocationCollision { from: u32, curr_to: u32, curr_module: RelocationModule, prev_to: u32, prev_module: RelocationModule },
57}
58
59impl Relocations {
60    pub fn new() -> Self {
61        Self { relocations: BTreeMap::new() }
62    }
63
64    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, RelocationsParseError> {
65        let path = path.as_ref();
66        let mut context = ParseContext { file_path: path.to_str().unwrap().to_string(), row: 0 };
67
68        let file = open_file(path)?;
69        let reader = BufReader::new(file);
70
71        let mut relocations = BTreeMap::new();
72        for line in reader.lines() {
73            context.row += 1;
74
75            let line = line?;
76            let comment_start = line.find("//").unwrap_or(line.len());
77            let line = &line[..comment_start];
78
79            let Some(relocation) = Relocation::parse(line, &context)? else {
80                continue;
81            };
82            relocations.insert(relocation.from, relocation);
83        }
84
85        Ok(Self { relocations })
86    }
87
88    pub fn to_file<P: AsRef<Path>>(&self, path: P) -> Result<(), RelocationsWriteError> {
89        let path = path.as_ref();
90
91        let file = create_file(path)?;
92        let mut writer = BufWriter::new(file);
93
94        for relocation in self.relocations.values() {
95            writeln!(writer, "{relocation}")?;
96        }
97        Ok(())
98    }
99
100    pub fn add(&mut self, relocation: Relocation) -> Result<&mut Relocation, RelocationsError> {
101        match self.relocations.entry(relocation.from) {
102            btree_map::Entry::Vacant(entry) => Ok(entry.insert(relocation)),
103            btree_map::Entry::Occupied(entry) => {
104                if entry.get() == &relocation {
105                    log::warn!(
106                        "Relocation from {:#010x} to {:#010x} in {} is identical to existing one",
107                        relocation.from,
108                        relocation.to,
109                        relocation.module
110                    );
111                    Ok(entry.into_mut())
112                } else {
113                    let other = entry.get();
114                    let error = RelocationCollisionSnafu {
115                        from: relocation.from,
116                        curr_to: relocation.to,
117                        curr_module: relocation.module,
118                        prev_to: other.to,
119                        prev_module: other.module.clone(),
120                    }
121                    .build();
122                    log::error!("{error}");
123                    Err(error)
124                }
125            }
126        }
127    }
128
129    pub fn add_call(
130        &mut self,
131        from: u32,
132        to: u32,
133        module: RelocationModule,
134        from_thumb: bool,
135        to_thumb: bool,
136    ) -> Result<&mut Relocation, RelocationsError> {
137        self.add(Relocation::new_call(from, to, module, from_thumb, to_thumb))
138    }
139
140    pub fn add_load(
141        &mut self,
142        from: u32,
143        to: u32,
144        addend: i32,
145        module: RelocationModule,
146    ) -> Result<&mut Relocation, RelocationsError> {
147        self.add(Relocation::new_load(from, to, addend, module))
148    }
149
150    pub fn get(&self, from: u32) -> Option<&Relocation> {
151        self.relocations.get(&from)
152    }
153
154    pub fn get_mut(&mut self, from: u32) -> Option<&mut Relocation> {
155        self.relocations.get_mut(&from)
156    }
157
158    pub fn iter(&self) -> impl Iterator<Item = &Relocation> {
159        self.relocations.values()
160    }
161
162    pub fn iter_range(&self, range: Range<u32>) -> impl Iterator<Item = (&u32, &Relocation)> {
163        self.relocations.range(range)
164    }
165}
166
167#[derive(PartialEq, Eq, Clone)]
168pub struct Relocation {
169    from: u32,
170    to: u32,
171    addend: i32,
172    kind: RelocationKind,
173    module: RelocationModule,
174    pub source: Option<String>,
175}
176
177#[derive(Debug, Snafu)]
178pub enum RelocationParseError {
179    #[snafu(display("{context}: failed to parse \"from\" address '{value}': {error}\n{backtrace}"))]
180    ParseFrom { context: ParseContext, value: String, error: ParseIntError, backtrace: Backtrace },
181    #[snafu(display("{context}: failed to parse \"to\" address '{value}': {error}\n{backtrace}"))]
182    ParseTo { context: ParseContext, value: String, error: ParseIntError, backtrace: Backtrace },
183    #[snafu(display("{context}: failed to parse \"add\" addend '{value}': {error}\n{backtrace}"))]
184    ParseAdd { context: ParseContext, value: String, error: ParseIntError, backtrace: Backtrace },
185    #[snafu(transparent)]
186    RelocationKindParse { source: RelocationKindParseError },
187    #[snafu(transparent)]
188    RelocationModuleParse { source: Box<RelocationModuleParseError> },
189    #[snafu(display(
190        "{context}: expected relocation attribute 'from', 'to', 'add', 'kind' or 'module' but got '{key}':\n{backtrace}"
191    ))]
192    UnknownAttribute { context: ParseContext, key: String, backtrace: Backtrace },
193    #[snafu(display("{context}: missing '{attribute}' attribute"))]
194    MissingAttribute { context: ParseContext, attribute: String, backtrace: Backtrace },
195    #[snafu(display("{context}: relocation to 'overlay_id' must have \"module:none\":\n{backtrace}"))]
196    OverlayIdWithModule { context: ParseContext, backtrace: Backtrace },
197}
198
199impl Relocation {
200    fn parse(line: &str, context: &ParseContext) -> Result<Option<Self>, RelocationParseError> {
201        let words = line.split_whitespace();
202
203        let mut from = None;
204        let mut to = None;
205        let mut addend = 0;
206        let mut kind = None;
207        let mut module = None;
208        for (key, value) in iter_attributes(words) {
209            match key {
210                "from" => from = Some(parse_u32(value).map_err(|error| ParseFromSnafu { context, value, error }.build())?),
211                "to" => to = Some(parse_u32(value).map_err(|error| ParseToSnafu { context, value, error }.build())?),
212                "add" => addend = parse_i32(value).map_err(|error| ParseAddSnafu { context, value, error }.build())?,
213                "kind" => kind = Some(RelocationKind::parse(value, context)?),
214                "module" => module = Some(RelocationModule::parse(value, context)?),
215                _ => return UnknownAttributeSnafu { context, key }.fail(),
216            }
217        }
218
219        let from = from.ok_or_else(|| MissingAttributeSnafu { context, attribute: "from" }.build())?;
220        let to = to.ok_or_else(|| MissingAttributeSnafu { context, attribute: "to" }.build())?;
221        let kind = kind.ok_or_else(|| MissingAttributeSnafu { context, attribute: "kind" }.build())?;
222        let module = module.ok_or_else(|| MissingAttributeSnafu { context, attribute: "module" }.build())?;
223
224        if kind == RelocationKind::OverlayId && module != RelocationModule::None {
225            return OverlayIdWithModuleSnafu { context }.fail();
226        }
227
228        Ok(Some(Self { from, to, addend, kind, module, source: None }))
229    }
230
231    pub fn new_call(from: u32, to: u32, module: RelocationModule, from_thumb: bool, to_thumb: bool) -> Self {
232        Self {
233            from,
234            to,
235            addend: 0,
236            kind: match (from_thumb, to_thumb) {
237                (true, true) => RelocationKind::ThumbCall,
238                (true, false) => RelocationKind::ThumbCallArm,
239                (false, true) => RelocationKind::ArmCallThumb,
240                (false, false) => RelocationKind::ArmCall,
241            },
242            module,
243            source: None,
244        }
245    }
246
247    pub fn new_branch(from: u32, to: u32, module: RelocationModule) -> Self {
248        Self { from, to, addend: 0, kind: RelocationKind::ArmBranch, module, source: None }
249    }
250
251    pub fn new_load(from: u32, to: u32, addend: i32, module: RelocationModule) -> Self {
252        Self { from, to, addend, kind: RelocationKind::Load, module, source: None }
253    }
254
255    pub fn from_address(&self) -> u32 {
256        self.from
257    }
258
259    pub fn to_address(&self) -> u32 {
260        self.to
261    }
262
263    pub fn kind(&self) -> RelocationKind {
264        self.kind
265    }
266
267    pub fn set_kind(&mut self, kind: RelocationKind) {
268        self.kind = kind;
269    }
270
271    pub fn module(&self) -> &RelocationModule {
272        &self.module
273    }
274
275    pub fn set_module(&mut self, module: RelocationModule) {
276        self.module = module;
277    }
278
279    pub fn destination_module(&self) -> Option<ModuleKind> {
280        match &self.module {
281            RelocationModule::None => None,
282            RelocationModule::Overlay { id } => Some(ModuleKind::Overlay(*id)),
283            RelocationModule::Overlays { .. } => None,
284            RelocationModule::Main => Some(ModuleKind::Arm9),
285            RelocationModule::Itcm => Some(ModuleKind::Autoload(AutoloadKind::Itcm)),
286            RelocationModule::Dtcm => Some(ModuleKind::Autoload(AutoloadKind::Dtcm)),
287            RelocationModule::Autoload { index } => Some(ModuleKind::Autoload(AutoloadKind::Unknown(*index))),
288        }
289    }
290
291    pub fn addend(&self) -> i64 {
292        self.addend as i64 + self.kind.addend()
293    }
294
295    pub fn addend_value(&self) -> i32 {
296        self.addend
297    }
298
299    pub fn set_addend(&mut self, addend: i32) {
300        self.addend = addend;
301    }
302
303    pub fn find_symbol_location<'a>(&self, symbol_map: &'a SymbolMap) -> Option<(&'a Symbol, u32)> {
304        let symbol = symbol_map
305            .first_symbol_before(self.from_address())
306            .and_then(|symbols| (!symbols.is_empty()).then_some(symbols[0].1))?;
307        let offset = self.from_address() - symbol.addr;
308
309        Some((symbol, offset))
310    }
311}
312
313impl Display for Relocation {
314    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
315        write!(f, "from:{:#010x} kind:{} ", self.from, self.kind)?;
316
317        if self.kind == RelocationKind::OverlayId {
318            write!(f, "to:{} ", self.to)?;
319        } else {
320            write!(f, "to:{:#010x} ", self.to)?;
321        }
322
323        if self.addend != 0 {
324            write!(f, "add:{:#x} ", self.addend)?;
325        }
326
327        write!(f, "module:{}", self.module)?;
328
329        if let Some(source) = &self.source {
330            write!(f, " // {source}")?;
331        }
332        Ok(())
333    }
334}
335
336#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
337pub enum RelocationKind {
338    ArmCall,
339    ThumbCall,
340    ArmCallThumb,
341    ThumbCallArm,
342    ArmBranch,
343    Load,
344    OverlayId,
345}
346
347#[derive(Debug, Snafu)]
348pub enum RelocationKindParseError {
349    #[snafu(display(
350        "{context}: unknown relocation kind '{value}', must be one of: arm_call, thumb_call, arm_call_thumb, thumb_call_arm, arm_branch, load:\n{backtrace}"
351    ))]
352    UnknownKind { context: ParseContext, value: String, backtrace: Backtrace },
353}
354
355impl RelocationKind {
356    fn parse(value: &str, context: &ParseContext) -> Result<Self, RelocationKindParseError> {
357        match value {
358            "arm_call" => Ok(Self::ArmCall),
359            "thumb_call" => Ok(Self::ThumbCall),
360            "arm_call_thumb" => Ok(Self::ArmCallThumb),
361            "thumb_call_arm" => Ok(Self::ThumbCallArm),
362            "arm_branch" => Ok(Self::ArmBranch),
363            "load" => Ok(Self::Load),
364            "overlay_id" => Ok(Self::OverlayId),
365            _ => UnknownKindSnafu { context, value }.fail(),
366        }
367    }
368
369    pub fn addend(&self) -> i64 {
370        match self {
371            Self::ArmCall => -8,
372            Self::ThumbCall => -4,
373            Self::ArmCallThumb => -8,
374            Self::ThumbCallArm => -4,
375            Self::ArmBranch => -8,
376            Self::Load => 0,
377            Self::OverlayId => 0,
378        }
379    }
380}
381
382impl Display for RelocationKind {
383    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
384        match self {
385            Self::ArmCall => write!(f, "arm_call"),
386            Self::ThumbCall => write!(f, "thumb_call"),
387            Self::ArmCallThumb => write!(f, "arm_call_thumb"),
388            Self::ThumbCallArm => write!(f, "thumb_call_arm"),
389            Self::ArmBranch => write!(f, "arm_branch"),
390            Self::Load => write!(f, "load"),
391            Self::OverlayId => write!(f, "overlay_id"),
392        }
393    }
394}
395
396#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)]
397pub enum RelocationModule {
398    None,
399    Overlay { id: u16 },
400    Overlays { ids: Vec<u16> },
401    Main,
402    Itcm,
403    Dtcm,
404    Autoload { index: u32 },
405}
406
407#[derive(Debug, Snafu)]
408pub enum RelocationFromModulesError {
409    #[snafu(display("Relocations to {module_kind} should be unambiguous:\n{backtrace}"))]
410    AmbiguousNonOverlayRelocation { module_kind: ModuleKind, backtrace: Backtrace },
411}
412
413#[derive(Debug, Snafu)]
414pub enum RelocationModuleParseError {
415    #[snafu(display("{context}: relocations to '{module}' have no options, but got '({options})':\n{backtrace}"))]
416    UnexpectedOptions { context: ParseContext, module: String, options: String, backtrace: Backtrace },
417    #[snafu(display("{context}: failed to parse overlay ID '{value}': {error}\n{backtrace}"))]
418    ParseOverlayId { context: ParseContext, value: String, error: ParseIntError, backtrace: Backtrace },
419    #[snafu(display("{context}: relocation to 'overlays' must have two or more overlay IDs, but got {ids:?}:\n{backtrace}"))]
420    ExpectedMultipleOverlays { context: ParseContext, ids: Vec<u16>, backtrace: Backtrace },
421    #[snafu(display("{context}: failed to parse autoload index '{value}': {error}\n{backtrace}"))]
422    ParseAutoloadIndex { context: ParseContext, value: String, error: ParseIntError, backtrace: Backtrace },
423    #[snafu(display(
424        "{context}: unknown relocation to '{module}', must be one of: overlays, overlay, main, itcm, dtcm, none:\n{backtrace}"
425    ))]
426    UnknownModule { context: ParseContext, module: String, backtrace: Backtrace },
427}
428
429impl RelocationModule {
430    pub fn from_modules<'a, I>(mut modules: I) -> Result<Self, RelocationFromModulesError>
431    where
432        I: Iterator<Item = &'a Module>,
433    {
434        let Some(first) = modules.next() else { return Ok(Self::None) };
435
436        let module_kind = first.kind();
437        match module_kind {
438            ModuleKind::Arm9 => {
439                if modules.next().is_some() {
440                    return AmbiguousNonOverlayRelocationSnafu { module_kind }.fail();
441                }
442                Ok(Self::Main)
443            }
444            ModuleKind::Autoload(kind) => {
445                if modules.next().is_some() {
446                    return AmbiguousNonOverlayRelocationSnafu { module_kind }.fail();
447                }
448                match kind {
449                    AutoloadKind::Itcm => Ok(Self::Itcm),
450                    AutoloadKind::Dtcm => Ok(Self::Dtcm),
451                    AutoloadKind::Unknown(index) => Ok(Self::Autoload { index }),
452                }
453            }
454            ModuleKind::Overlay(id) => {
455                let ids = iter::once(first)
456                    .chain(modules)
457                    .map(|module| {
458                        if let ModuleKind::Overlay(id) = module.kind() {
459                            Ok(id)
460                        } else {
461                            AmbiguousNonOverlayRelocationSnafu { module_kind: module.kind() }.fail()
462                        }
463                    })
464                    .collect::<Result<Vec<_>, _>>()?;
465                if ids.len() > 1 {
466                    Ok(Self::Overlays { ids })
467                } else {
468                    Ok(Self::Overlay { id })
469                }
470            }
471        }
472    }
473
474    fn parse(text: &str, context: &ParseContext) -> Result<Self, Box<RelocationModuleParseError>> {
475        let (value, options) = text.split_once('(').unwrap_or((text, ""));
476        let options = options.strip_suffix(')').unwrap_or(options);
477
478        match value {
479            "none" => {
480                if options.is_empty() {
481                    Ok(Self::None)
482                } else {
483                    Err(Box::new(UnexpectedOptionsSnafu { context, module: "none", options }.build()))
484                }
485            }
486            "overlay" => Ok(Self::Overlay {
487                id: parse_u16(options).map_err(|error| ParseOverlayIdSnafu { context, value: options, error }.build())?,
488            }),
489            "overlays" => {
490                let ids = options
491                    .split(',')
492                    .map(|x| parse_u16(x).map_err(|error| ParseOverlayIdSnafu { context, value: x, error }.build()))
493                    .collect::<Result<Vec<_>, _>>()?;
494                if ids.len() < 2 {
495                    Err(Box::new(ExpectedMultipleOverlaysSnafu { context, ids }.build()))
496                } else {
497                    Ok(Self::Overlays { ids })
498                }
499            }
500            "main" => {
501                if options.is_empty() {
502                    Ok(Self::Main)
503                } else {
504                    Err(Box::new(UnexpectedOptionsSnafu { context, module: "main", options }.build()))
505                }
506            }
507            "itcm" => {
508                if options.is_empty() {
509                    Ok(Self::Itcm)
510                } else {
511                    Err(Box::new(UnexpectedOptionsSnafu { context, module: "itcm", options }.build()))
512                }
513            }
514            "dtcm" => {
515                if options.is_empty() {
516                    Ok(Self::Dtcm)
517                } else {
518                    Err(Box::new(UnexpectedOptionsSnafu { context, module: "dtcm", options }.build()))
519                }
520            }
521            "autoload" => Ok(Self::Autoload {
522                index: parse_u32(options)
523                    .map_err(|error| ParseAutoloadIndexSnafu { context, value: options, error }.build())?,
524            }),
525            _ => Err(Box::new(UnknownModuleSnafu { context, module: value }.build())),
526        }
527    }
528}
529
530impl From<ModuleKind> for RelocationModule {
531    fn from(value: ModuleKind) -> Self {
532        match value {
533            ModuleKind::Arm9 => Self::Main,
534            ModuleKind::Overlay(id) => Self::Overlay { id },
535            ModuleKind::Autoload(kind) => match kind {
536                AutoloadKind::Itcm => Self::Itcm,
537                AutoloadKind::Dtcm => Self::Dtcm,
538                AutoloadKind::Unknown(index) => Self::Autoload { index },
539            },
540        }
541    }
542}
543
544impl Display for RelocationModule {
545    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
546        match self {
547            RelocationModule::None => write!(f, "none"),
548            RelocationModule::Overlay { id } => write!(f, "overlay({id})"),
549            RelocationModule::Overlays { ids } => {
550                write!(f, "overlays({}", ids[0])?;
551                for id in &ids[1..] {
552                    write!(f, ",{id}")?;
553                }
554                write!(f, ")")?;
555                Ok(())
556            }
557            RelocationModule::Main => write!(f, "main"),
558            RelocationModule::Itcm => write!(f, "itcm"),
559            RelocationModule::Dtcm => write!(f, "dtcm"),
560            RelocationModule::Autoload { index } => write!(f, "autoload({index})"),
561        }
562    }
563}