use std::{borrow::Cow, collections::HashMap, path::PathBuf};
use serde::Deserialize;
use crate::{
absent_nullable::AbsentNullable,
file_info::{FileInfo, FileInfoSerial},
gp_info::{GpInfo, GpInfoSerial},
traits::Serial,
utils, EscapedPath, KeepSections, RuntimeSettings, Settings, SlinkyError,
};
#[derive(PartialEq, Debug, Clone)]
#[non_exhaustive]
pub struct Segment {
pub name: String,
pub files: Vec<FileInfo>,
pub fixed_vram: Option<u32>,
pub fixed_symbol: Option<String>,
pub follows_segment: Option<String>,
pub vram_class: Option<String>,
pub dir: PathBuf,
pub gp_info: Option<GpInfo>,
pub include_if_any: Vec<(String, String)>,
pub include_if_all: Vec<(String, String)>,
pub exclude_if_any: Vec<(String, String)>,
pub exclude_if_all: Vec<(String, String)>,
pub alloc_sections: Option<Vec<String>>,
pub noload_sections: Option<Vec<String>>,
pub subalign: Option<u32>,
pub segment_start_align: Option<u32>,
pub segment_end_align: Option<u32>,
pub section_start_align: Option<u32>,
pub section_end_align: Option<u32>,
pub sections_start_alignment: HashMap<String, u32>,
pub sections_end_alignment: HashMap<String, u32>,
pub wildcard_sections: bool,
pub fill_value: Option<u32>,
pub sections_subgroups: HashMap<String, Vec<String>>,
pub keep_sections: KeepSections,
}
impl Segment {
pub fn clone_with_new_files(&self, new_files: Vec<FileInfo>) -> Self {
Self {
name: self.name.clone(),
files: new_files,
fixed_vram: self.fixed_vram,
fixed_symbol: self.fixed_symbol.clone(),
follows_segment: self.follows_segment.clone(),
vram_class: self.vram_class.clone(),
dir: self.dir.clone(),
gp_info: self.gp_info.clone(),
include_if_any: self.include_if_any.clone(),
include_if_all: self.include_if_all.clone(),
exclude_if_any: self.exclude_if_any.clone(),
exclude_if_all: self.exclude_if_all.clone(),
alloc_sections: self.alloc_sections.clone(),
noload_sections: self.noload_sections.clone(),
subalign: self.subalign,
segment_start_align: self.segment_start_align,
segment_end_align: self.segment_end_align,
section_start_align: self.section_start_align,
section_end_align: self.section_end_align,
sections_start_alignment: self.sections_start_alignment.clone(),
sections_end_alignment: self.sections_end_alignment.clone(),
wildcard_sections: self.wildcard_sections,
fill_value: self.fill_value,
sections_subgroups: self.sections_subgroups.clone(),
keep_sections: self.keep_sections.clone(),
}
}
pub fn pass_down_keep_sections(&mut self, keep_sections: &KeepSections) {
if *keep_sections == KeepSections::Absent {
return;
}
if self.keep_sections == KeepSections::Absent {
self.keep_sections.clone_from(keep_sections);
self.files
.iter_mut()
.for_each(|f| f.pass_down_keep_sections(keep_sections));
}
}
}
impl Segment {
pub fn dir_escaped(&self, rs: &RuntimeSettings) -> Result<EscapedPath, SlinkyError> {
rs.escape_path(&self.dir)
}
}
#[derive(Deserialize, PartialEq, Debug)]
#[serde(deny_unknown_fields)]
pub(crate) struct SegmentSerial {
pub name: String,
pub files: Vec<FileInfoSerial>,
#[serde(default)]
pub fixed_vram: AbsentNullable<u32>,
#[serde(default)]
pub fixed_symbol: AbsentNullable<String>,
#[serde(default)]
pub follows_segment: AbsentNullable<String>,
#[serde(default)]
pub vram_class: AbsentNullable<String>,
#[serde(default)]
pub dir: AbsentNullable<PathBuf>,
#[serde(default)]
pub gp_info: AbsentNullable<GpInfoSerial>,
#[serde(default)]
pub include_if_any: AbsentNullable<Vec<(String, String)>>,
#[serde(default)]
pub include_if_all: AbsentNullable<Vec<(String, String)>>,
#[serde(default)]
pub exclude_if_any: AbsentNullable<Vec<(String, String)>>,
#[serde(default)]
pub exclude_if_all: AbsentNullable<Vec<(String, String)>>,
#[serde(default)]
pub alloc_sections: AbsentNullable<Vec<String>>,
#[serde(default)]
pub noload_sections: AbsentNullable<Vec<String>>,
#[serde(default)]
pub subalign: AbsentNullable<u32>,
#[serde(default)]
pub segment_start_align: AbsentNullable<u32>,
#[serde(default)]
pub segment_end_align: AbsentNullable<u32>,
#[serde(default)]
pub section_start_align: AbsentNullable<u32>,
#[serde(default)]
pub section_end_align: AbsentNullable<u32>,
#[serde(default)]
pub sections_start_alignment: AbsentNullable<HashMap<String, u32>>,
#[serde(default)]
pub sections_end_alignment: AbsentNullable<HashMap<String, u32>>,
#[serde(default)]
pub wildcard_sections: AbsentNullable<bool>,
#[serde(default)]
pub fill_value: AbsentNullable<u32>,
#[serde(default)]
pub sections_subgroups: AbsentNullable<HashMap<String, Vec<String>>>,
#[serde(default)]
pub keep_sections: KeepSections,
}
impl Serial for SegmentSerial {
type Output = Segment;
fn unserialize(self, settings: &Settings) -> Result<Self::Output, SlinkyError> {
if self.name.is_empty() {
return Err(SlinkyError::EmptyValue {
name: "name".to_string(),
});
}
let name = self.name;
if self.files.is_empty() {
return Err(SlinkyError::EmptyValue {
name: "files".to_string(),
});
}
let mut files = self.files.unserialize(settings)?;
let fixed_vram = self.fixed_vram.get_non_null_no_default("fixed_vram")?;
let fixed_symbol = self.fixed_symbol.get_non_null_no_default("fixed_symbol")?;
let follows_segment = self
.follows_segment
.get_non_null_no_default("follows_segment")?;
let vram_class = self.vram_class.get_non_null_no_default("vram_class")?;
if fixed_vram.is_some() {
if fixed_symbol.is_some() {
return Err(SlinkyError::InvalidFieldCombo {
field1: "fixed_vram".to_string(),
field2: "fixed_symbol".to_string(),
});
}
if follows_segment.is_some() {
return Err(SlinkyError::InvalidFieldCombo {
field1: "fixed_vram".to_string(),
field2: "follows_segment".to_string(),
});
}
if vram_class.is_some() {
return Err(SlinkyError::InvalidFieldCombo {
field1: "fixed_vram".to_string(),
field2: "vram_class".to_string(),
});
}
}
if fixed_symbol.is_some() {
if follows_segment.is_some() {
return Err(SlinkyError::InvalidFieldCombo {
field1: "fixed_symbol".to_string(),
field2: "follows_segment".to_string(),
});
}
if vram_class.is_some() {
return Err(SlinkyError::InvalidFieldCombo {
field1: "fixed_symbol".to_string(),
field2: "vram_class".to_string(),
});
}
}
if follows_segment.is_some() && vram_class.is_some() {
return Err(SlinkyError::InvalidFieldCombo {
field1: "follows_segment".to_string(),
field2: "vram_class".to_string(),
});
}
let dir = self.dir.get_non_null("dir", PathBuf::new)?;
let gp_info = self
.gp_info
.get_non_null_no_default("gp_info")?
.unserialize(settings)?;
if gp_info.is_some() && settings.hardcoded_gp_value.is_some() {
return Err(SlinkyError::InvalidFieldCombo {
field1: "segment.gp_info".to_string(),
field2: "settings.hardcoded_gp_value".to_string(),
});
}
let include_if_any = self
.include_if_any
.get_non_null_not_empty("include_if_any", Vec::new)?;
let include_if_all = self
.include_if_all
.get_non_null_not_empty("include_if_all", Vec::new)?;
let exclude_if_any = self
.exclude_if_any
.get_non_null_not_empty("exclude_if_any", Vec::new)?;
let exclude_if_all = self
.exclude_if_all
.get_non_null_not_empty("exclude_if_all", Vec::new)?;
let alloc_sections = self
.alloc_sections
.get_optional_nullable("alloc_sections", || settings.alloc_sections.clone())?;
let noload_sections = self
.noload_sections
.get_optional_nullable("noload_sections", || settings.noload_sections.clone())?;
if let Some(gp) = &gp_info {
if utils::is_none_or(alloc_sections.as_ref(), |s| !s.contains(&gp.section))
&& utils::is_none_or(noload_sections.as_ref(), |s| !s.contains(&gp.section))
{
return Err(SlinkyError::MissingSectionForSegment {
field_name: Cow::from("gp_info"),
section: Cow::from(gp_info.unwrap().section),
segment: Cow::from(name),
});
}
}
let subalign = self
.subalign
.get_optional_nullable("subalign", || settings.subalign)?;
let segment_start_align = self
.segment_start_align
.get_optional_nullable("segment_start_align", || settings.segment_start_align)?;
let segment_end_align = self
.segment_end_align
.get_optional_nullable("segment_end_align", || settings.segment_end_align)?;
let section_start_align = self
.section_start_align
.get_optional_nullable("section_start_align", || settings.section_start_align)?;
let section_end_align = self
.section_end_align
.get_optional_nullable("section_end_align", || settings.section_end_align)?;
let sections_start_alignment = self
.sections_start_alignment
.get_non_null("sections_start_alignment", || {
settings.sections_start_alignment.clone()
})?;
let sections_end_alignment = self
.sections_end_alignment
.get_non_null("sections_end_alignment", || {
settings.sections_end_alignment.clone()
})?;
let wildcard_sections = self
.wildcard_sections
.get_non_null("wildcard_sections", || settings.wildcard_sections)?;
let fill_value = self
.fill_value
.get_optional_nullable("fill_value", || settings.fill_value)?;
let keep_sections = self.keep_sections;
let sections_subgroups = self
.sections_subgroups
.get_non_null("sections_subgroups", || settings.sections_subgroups.clone())?;
if keep_sections != KeepSections::Absent {
files
.iter_mut()
.for_each(|f| f.pass_down_keep_sections(&keep_sections));
}
Ok(Self::Output {
name,
files,
fixed_vram,
fixed_symbol,
follows_segment,
vram_class,
dir,
gp_info,
include_if_any,
include_if_all,
exclude_if_any,
exclude_if_all,
alloc_sections,
noload_sections,
subalign,
segment_start_align,
segment_end_align,
section_start_align,
section_end_align,
sections_start_alignment,
sections_end_alignment,
wildcard_sections,
fill_value,
sections_subgroups,
keep_sections,
})
}
}