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 pub name: String,
127 pub object: PathBuf,
129 pub hash: String,
131 pub delinks: PathBuf,
133 pub symbols: PathBuf,
135 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}