#[cfg(feature = "std")]
use std::borrow::Cow;
#[cfg(not(feature = "std"))]
use alloc::borrow::Cow;
#[cfg(not(feature = "std"))]
use alloc::string::{String, ToString};
use onerom_config::fw::FirmwareVersion;
use onerom_config::hw::Board;
use onerom_metadata::{OneromRomInfo, OneromRomSlot, RomSlotType};
use crate::ParseError;
use crate::info::{Sdrr, SdrrRomInfo, SdrrRomSet};
use crate::onerom::OneRom;
use crate::types::SdrrRomType;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum ParsedDevice {
Original(Sdrr),
Schema(OneRom),
}
impl ParsedDevice {
pub fn is_running(&self) -> bool {
match self {
Self::Original(sdrr) => sdrr.is_running(),
Self::Schema(onerom) => onerom.runtime().is_some(),
}
}
pub fn version(&self) -> Option<FirmwareVersion> {
match self {
Self::Original(sdrr) => sdrr.flash.as_ref().map(|f| f.version),
Self::Schema(onerom) => {
let info = onerom.info()?;
Some(FirmwareVersion::new(
info.major_version,
info.minor_version,
info.patch_version,
info.build_number,
))
}
}
}
pub fn parse_errors(&self) -> &[ParseError] {
match self {
Self::Original(sdrr) => sdrr
.flash
.as_ref()
.map(|f| f.parse_errors.as_slice())
.unwrap_or(&[]),
Self::Schema(onerom) => onerom.parse_errors(),
}
}
pub fn as_original(&self) -> Option<&Sdrr> {
if let Self::Original(sdrr) = self {
Some(sdrr)
} else {
None
}
}
pub fn as_schema(&self) -> Option<&OneRom> {
if let Self::Schema(onerom) = self {
Some(onerom)
} else {
None
}
}
pub fn get_board(&self) -> Option<Board> {
match self {
Self::Original(sdrr) => sdrr.flash.as_ref().and_then(|f| f.board),
Self::Schema(onerom) => {
let hw_rev = &onerom.info()?.metadata.as_ref()?.hw.hw_rev;
Board::try_from_str(hw_rev)
}
}
}
pub fn is_usb_run_capable(&self) -> bool {
match self {
Self::Original(sdrr) => sdrr
.flash
.as_ref()
.map(|f| f.is_usb_run_capable())
.unwrap_or(false),
Self::Schema(onerom) => {
let slot = match onerom.metadata() {
Some(metadata) => match metadata.rom_slots.first() {
Some(slot) => slot,
None => return false,
},
None => return false,
};
let rom_info = match slot.roms.first() {
Some(info) => info,
None => return false,
};
rom_info.rbcp_rom_type
== onerom_config::chip::ChipType::SystemPlugin.rbcp_chip_type()
}
}
}
pub fn mcu_name(&self) -> Option<String> {
match self {
Self::Original(sdrr) => sdrr
.flash
.as_ref()
.and_then(|f| f.mcu_variant)
.map(|v| v.to_string()),
Self::Schema(_) => self.get_board().map(|b| b.mcu_family().to_string()),
}
}
pub fn active_slot_index(&self) -> Option<usize> {
match self {
Self::Original(sdrr) => sdrr.ram.as_ref().map(|r| r.rom_set_index as usize),
Self::Schema(onerom) => onerom.runtime().map(|r| r.rom_slot_index as usize),
}
}
pub fn slots(&self) -> Slots<'_> {
let active = self.active_slot_index();
let inner = match self {
Self::Original(sdrr) => match sdrr.flash.as_ref() {
Some(info) => SlotsInner::Original(info.rom_sets.iter()),
None => SlotsInner::Empty,
},
Self::Schema(onerom) => match onerom.metadata() {
Some(md) => SlotsInner::Schema(md.rom_slots.iter()),
None => SlotsInner::Empty,
},
};
Slots {
inner,
active,
idx: 0,
user_idx: 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SlotKind {
Plugin,
Rom,
}
#[derive(Debug)]
pub struct RomView<'a> {
pub rom_type: Cow<'a, str>,
pub filename: Option<&'a str>,
pub size: usize,
}
#[derive(Debug)]
pub struct SlotView<'a> {
pub slot_index: usize,
pub user_index: Option<usize>,
pub kind: SlotKind,
pub active: bool,
roms: RomsBacking<'a>,
}
impl<'a> SlotView<'a> {
pub fn roms(&self) -> RomsIter<'a> {
match self.roms {
RomsBacking::Original(s) => RomsIter::Original(s.iter()),
RomsBacking::Schema(s) => RomsIter::Schema(s.iter()),
}
}
}
#[derive(Clone, Copy, Debug)]
enum RomsBacking<'a> {
Original(&'a [SdrrRomInfo]),
Schema(&'a [OneromRomInfo]),
}
pub enum RomsIter<'a> {
#[doc(hidden)]
Original(core::slice::Iter<'a, SdrrRomInfo>),
#[doc(hidden)]
Schema(core::slice::Iter<'a, OneromRomInfo>),
}
impl<'a> Iterator for RomsIter<'a> {
type Item = RomView<'a>;
fn next(&mut self) -> Option<Self::Item> {
match self {
RomsIter::Original(it) => it.next().map(|r| RomView {
rom_type: Cow::Owned(r.rom_type.to_string()),
filename: r.filename.as_deref(),
size: r.rom_type.rom_size(),
}),
RomsIter::Schema(it) => it.next().map(|r| RomView {
rom_type: Cow::Borrowed(r.rom_type.as_str()),
filename: r.filename.as_deref(),
size: r.chip_size as usize,
}),
}
}
}
pub struct Slots<'a> {
inner: SlotsInner<'a>,
active: Option<usize>,
idx: usize,
user_idx: usize,
}
enum SlotsInner<'a> {
Original(core::slice::Iter<'a, SdrrRomSet>),
Schema(core::slice::Iter<'a, OneromRomSlot>),
Empty,
}
impl<'a> Iterator for Slots<'a> {
type Item = SlotView<'a>;
fn next(&mut self) -> Option<Self::Item> {
let (kind, roms) = match &mut self.inner {
SlotsInner::Original(it) => {
let set = it.next()?;
(sdrr_set_kind(set), RomsBacking::Original(&set.roms))
}
SlotsInner::Schema(it) => {
let slot = it.next()?;
(schema_slot_kind(slot), RomsBacking::Schema(&slot.roms))
}
SlotsInner::Empty => return None,
};
let slot_index = self.idx;
self.idx += 1;
let user_index = match kind {
SlotKind::Rom => {
let u = self.user_idx;
self.user_idx += 1;
Some(u)
}
SlotKind::Plugin => None,
};
Some(SlotView {
slot_index,
user_index,
kind,
active: self.active == Some(slot_index),
roms,
})
}
}
fn sdrr_set_kind(set: &SdrrRomSet) -> SlotKind {
match set.roms.first().map(|r| r.rom_type) {
Some(SdrrRomType::SystemPlugin | SdrrRomType::UserPlugin | SdrrRomType::PioPlugin) => {
SlotKind::Plugin
}
_ => SlotKind::Rom,
}
}
fn schema_slot_kind(slot: &OneromRomSlot) -> SlotKind {
match slot.slot_type {
RomSlotType::RomSlotTypePluginSystem
| RomSlotType::RomSlotTypePluginUser
| RomSlotType::RomSlotTypePluginPio => SlotKind::Plugin,
_ => SlotKind::Rom,
}
}