ds_decomp/config/
relocations.rs

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