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: serde_yml::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_yml::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) => {
65                self.autoloads.iter().find(|autoload| autoload.kind == autoload_kind).map(|autoload| &autoload.module)
66            }
67            ModuleKind::Overlay(id) => self.overlays.iter().find(|overlay| overlay.id == id).map(|overlay| &overlay.module),
68        }
69    }
70
71    pub fn load_module<P: AsRef<Path>>(
72        &self,
73        config_path: P,
74        symbol_maps: &mut SymbolMaps,
75        module_kind: ModuleKind,
76        rom: &Rom,
77    ) -> Result<Module, LoadModuleError> {
78        let config_path = config_path.as_ref();
79        let symbol_map = symbol_maps.get_mut(module_kind);
80        let module_config =
81            self.get_module_config_by_kind(module_kind).ok_or_else(|| ModuleConfigNotFoundSnafu { module_kind }.build())?;
82        let relocations = Relocations::from_file(config_path.join(&module_config.relocations))?;
83        let delinks = Delinks::from_file(config_path.join(&module_config.delinks), module_kind)?;
84        let code = rom.get_code(module_kind)?;
85
86        let module = Module::new(
87            symbol_map,
88            ModuleOptions {
89                kind: module_kind,
90                name: module_config.name.clone(),
91                relocations,
92                sections: delinks.sections,
93                code: &code,
94                signed: false,
95            },
96        )?;
97
98        Ok(module)
99    }
100
101    pub fn load_rom<P: AsRef<Path>>(&self, config_path: P) -> Result<Rom, RomSaveError> {
102        let config_path = config_path.as_ref();
103        Rom::load(
104            config_path.join(&self.rom_config),
105            RomLoadOptions {
106                key: None,
107                compress: false,
108                encrypt: false,
109                load_files: false,
110                load_header: false,
111                load_banner: false,
112            },
113        )
114    }
115}
116
117#[derive(Serialize, Deserialize)]
118pub struct ConfigModule {
119    /// Name of module
120    pub name: String,
121    /// Binary file to build
122    pub object: PathBuf,
123    /// 64-bit fxhash of the binary file
124    pub hash: String,
125    /// Path to delinks file
126    pub delinks: PathBuf,
127    /// Path to symbols file
128    pub symbols: PathBuf,
129    /// Path to relocs file
130    pub relocations: PathBuf,
131}
132
133#[derive(Serialize, Deserialize)]
134pub struct ConfigOverlay {
135    pub id: u16,
136    #[serde(default = "default_overlay_signed", skip_serializing_if = "skip_overlay_signed")]
137    pub signed: bool,
138    #[serde(flatten)]
139    pub module: ConfigModule,
140}
141
142fn default_overlay_signed() -> bool {
143    false
144}
145
146fn skip_overlay_signed(signed: &bool) -> bool {
147    *signed == default_overlay_signed()
148}
149
150#[derive(Serialize, Deserialize)]
151pub struct ConfigAutoload {
152    pub kind: AutoloadKind,
153    #[serde(flatten)]
154    pub module: ConfigModule,
155}