Skip to main content

ds_decomp/config/
config.rs

1use std::{
2    backtrace::Backtrace,
3    path::{Path, PathBuf},
4};
5
6use ds_rom::rom::{Rom, RomLoadOptions, RomSaveError, raw::AutoloadKind};
7use serde::{Deserialize, Serialize};
8use snafu::Snafu;
9
10use crate::{
11    config::{
12        delinks::{Delinks, DelinksParseError},
13        module::{Module, ModuleError, ModuleKind, ModuleOptions},
14        relocations::{Relocations, RelocationsParseError},
15        symbol::SymbolMaps,
16    },
17    rom::rom::{RomExt, RomGetCodeError},
18    util::io::{FileError, open_file},
19};
20
21#[derive(Serialize, Deserialize)]
22pub struct Config {
23    pub rom_config: PathBuf,
24    pub build_path: PathBuf,
25    pub delinks_path: PathBuf,
26    pub main_module: ConfigModule,
27    pub autoloads: Vec<ConfigAutoload>,
28    pub overlays: Vec<ConfigOverlay>,
29}
30
31#[derive(Debug, Snafu)]
32pub enum ConfigParseError {
33    #[snafu(transparent)]
34    File { source: FileError },
35    #[snafu(display("Failed to parse dsd config file '{}': {error}\n{backtrace}", path.display()))]
36    SerdeYml { path: PathBuf, error: Box<serde_saphyr::Error>, backtrace: Backtrace },
37}
38
39#[derive(Debug, Snafu)]
40pub enum LoadModuleError {
41    #[snafu(display("Failed to load module config for kind {module_kind}: \n{backtrace}"))]
42    ModuleConfigNotFound { module_kind: ModuleKind, backtrace: Backtrace },
43    #[snafu(transparent)]
44    RelocationsParse { source: RelocationsParseError },
45    #[snafu(transparent)]
46    DelinksParse { source: DelinksParseError },
47    #[snafu(transparent)]
48    File { source: FileError },
49    #[snafu(transparent)]
50    Module { source: ModuleError },
51    #[snafu(transparent)]
52    RomGetCode { source: RomGetCodeError },
53}
54
55impl Config {
56    pub fn from_file(path: &Path) -> Result<Config, ConfigParseError> {
57        let file = open_file(path)?;
58        serde_saphyr::from_reader(file).map_err(|error| SerdeYmlSnafu { path, error }.build())
59    }
60
61    pub fn get_module_config_by_kind(&self, module_kind: ModuleKind) -> Option<&ConfigModule> {
62        match module_kind {
63            ModuleKind::Arm9 => Some(&self.main_module),
64            ModuleKind::Autoload(autoload_kind) => self
65                .autoloads
66                .iter()
67                .find(|autoload| autoload.kind == autoload_kind)
68                .map(|autoload| &autoload.module),
69            ModuleKind::Overlay(id) => {
70                self.overlays.iter().find(|overlay| overlay.id == id).map(|overlay| &overlay.module)
71            }
72        }
73    }
74
75    pub fn load_module<P: AsRef<Path>>(
76        &self,
77        config_path: P,
78        symbol_maps: &mut SymbolMaps,
79        module_kind: ModuleKind,
80        rom: &Rom,
81    ) -> Result<Module, LoadModuleError> {
82        let config_path = config_path.as_ref();
83        let symbol_map = symbol_maps.get_mut(module_kind);
84        let module_config = self
85            .get_module_config_by_kind(module_kind)
86            .ok_or_else(|| ModuleConfigNotFoundSnafu { module_kind }.build())?;
87        let relocations = Relocations::from_file(config_path.join(&module_config.relocations))?;
88        let delinks = Delinks::from_file(config_path.join(&module_config.delinks), module_kind)?;
89        let code = rom.get_code(module_kind)?;
90
91        let module = Module::new(symbol_map, ModuleOptions {
92            kind: module_kind,
93            name: module_config.name.clone(),
94            relocations,
95            sections: delinks.sections,
96            code: &code,
97            signed: false,
98        })?;
99
100        Ok(module)
101    }
102
103    pub fn load_rom<P: AsRef<Path>>(&self, config_path: P) -> Result<Rom<'_>, RomSaveError> {
104        let config_path = config_path.as_ref();
105        Rom::load(config_path.join(&self.rom_config), RomLoadOptions {
106            key: None,
107            compress: false,
108            encrypt: false,
109            load_files: false,
110            load_header: false,
111            load_banner: false,
112            load_multiboot_signature: false,
113        })
114    }
115
116    pub fn iter_modules(&self) -> impl Iterator<Item = (ModuleKind, &ConfigModule)> {
117        std::iter::once((ModuleKind::Arm9, &self.main_module))
118            .chain(self.autoloads.iter().map(|a| (ModuleKind::Autoload(a.kind), &a.module)))
119            .chain(self.overlays.iter().map(|o| (ModuleKind::Overlay(o.id), &o.module)))
120    }
121}
122
123#[derive(Serialize, Deserialize)]
124pub struct ConfigModule {
125    /// Name of module
126    pub name: String,
127    /// Binary file to build
128    pub object: PathBuf,
129    /// 64-bit fxhash of the binary file
130    pub hash: String,
131    /// Path to delinks file
132    pub delinks: PathBuf,
133    /// Path to symbols file
134    pub symbols: PathBuf,
135    /// Path to relocs file
136    pub relocations: PathBuf,
137}
138
139#[derive(Serialize, Deserialize)]
140pub struct ConfigOverlay {
141    pub id: u16,
142    #[serde(default = "default_overlay_signed", skip_serializing_if = "skip_overlay_signed")]
143    pub signed: bool,
144    #[serde(flatten)]
145    pub module: ConfigModule,
146}
147
148fn default_overlay_signed() -> bool {
149    false
150}
151
152fn skip_overlay_signed(signed: &bool) -> bool {
153    *signed == default_overlay_signed()
154}
155
156#[derive(Serialize, Deserialize)]
157pub struct ConfigAutoload {
158    pub kind: AutoloadKind,
159    #[serde(flatten)]
160    pub module: ConfigModule,
161}