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 pub name: String,
121 pub object: PathBuf,
123 pub hash: String,
125 pub delinks: PathBuf,
127 pub symbols: PathBuf,
129 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}