use binrw::{binread, BinRead, BinReaderExt};
use macintosh_utils::PascalString;
use num_enum::{FromPrimitive, IntoPrimitive};
use resource_fork::Resource;
use crate::{
common::{long_list_plus_one, LengthPrefixedBuffer, Rect},
Clut,
};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UnknownDialogItem {
pub bounds: Rect,
pub unknown: [u8; 4],
pub enabled: bool,
pub item_type: u8,
pub payload: Vec<u8>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UserItem {
pub bounds: Rect,
pub unknown: [u8; 4],
pub enabled: bool,
pub payload: Vec<u8>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct HelpItem {
pub bounds: Rect,
pub unknown: [u8; 4],
pub enabled: bool,
pub payload: Vec<u8>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Button {
pub bounds: Rect,
pub unknown: [u8; 4],
pub enabled: bool,
pub label: String,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CheckBox {
pub bounds: Rect,
pub unknown: [u8; 4],
pub enabled: bool,
pub label: String,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RadioButton {
pub bounds: Rect,
pub unknown: [u8; 4],
pub enabled: bool,
pub label: String,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct StaticText {
pub bounds: Rect,
pub unknown: [u8; 4],
pub enabled: bool,
pub label: String,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TextField {
pub bounds: Rect,
pub unknown: [u8; 4],
pub enabled: bool,
pub label: String,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Control {
pub bounds: Rect,
pub unknown: [u8; 4],
pub enabled: bool,
pub payload: Vec<u8>,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Icon {
pub bounds: Rect,
pub unknown: [u8; 4],
pub enabled: bool,
pub resource: u16,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Picture {
pub bounds: Rect,
pub unknown: [u8; 4],
pub enabled: bool,
pub resource: u16,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum DialogItem {
Unknown(UnknownDialogItem),
UserItem(UserItem),
HelpItem(HelpItem),
Button(Button),
CheckBox(CheckBox),
RadioButton(RadioButton),
Control(Control),
StaticText(StaticText),
TextField(TextField),
Icon(Icon),
Picture(Picture),
}
impl DialogItem {
pub fn enabled(&self) -> bool {
match self {
DialogItem::Unknown(item) => item.enabled,
DialogItem::UserItem(item) => item.enabled,
DialogItem::HelpItem(item) => item.enabled,
DialogItem::Button(item) => item.enabled,
DialogItem::CheckBox(item) => item.enabled,
DialogItem::RadioButton(item) => item.enabled,
DialogItem::Control(item) => item.enabled,
DialogItem::StaticText(item) => item.enabled,
DialogItem::TextField(item) => item.enabled,
DialogItem::Icon(item) => item.enabled,
DialogItem::Picture(item) => item.enabled,
}
}
pub fn bounds(&self) -> Rect {
match self {
DialogItem::Unknown(item) => item.bounds,
DialogItem::UserItem(item) => item.bounds,
DialogItem::HelpItem(item) => item.bounds,
DialogItem::Button(item) => item.bounds,
DialogItem::CheckBox(item) => item.bounds,
DialogItem::RadioButton(item) => item.bounds,
DialogItem::Control(item) => item.bounds,
DialogItem::StaticText(item) => item.bounds,
DialogItem::TextField(item) => item.bounds,
DialogItem::Icon(item) => item.bounds,
DialogItem::Picture(item) => item.bounds,
}
}
}
#[derive(IntoPrimitive, FromPrimitive)]
#[repr(u8)]
enum ItemType {
UserItem = 0,
HelpItem = 1,
Button = 4,
CheckBox = 5,
RadioButton = 6,
Control = 7,
StaticText = 8,
TextField = 16,
Icon = 32,
Picture = 64,
#[num_enum(catch_all)]
Unknown(u8),
}
impl BinRead for DialogItem {
type Args<'a> = ();
fn read_options<R: std::io::Read + std::io::Seek>(
reader: &mut R,
_: binrw::Endian,
_: Self::Args<'_>,
) -> binrw::BinResult<Self> {
let unknown: [u8; 4] = reader.read_be()?;
let bounds: Rect = reader.read_be()?;
let enabled_and_type: u8 = reader.read_be()?;
let enabled = enabled_and_type & 0x80 != 0;
let item_type = enabled_and_type & !0x80;
let item_type = ItemType::from(item_type);
let result = match item_type {
ItemType::Button => Ok(Self::Button(Button {
bounds,
enabled,
unknown,
label: reader.read_be::<PascalString>()?.into(),
})),
ItemType::StaticText => Ok(Self::StaticText(StaticText {
bounds,
enabled,
unknown,
label: reader.read_be::<PascalString>()?.into(),
})),
ItemType::TextField => Ok(Self::TextField(TextField {
bounds,
enabled,
unknown,
label: reader.read_be::<PascalString>()?.into(),
})),
ItemType::Control => Ok(Self::Control(Control {
bounds,
enabled,
unknown,
payload: reader.read_be::<LengthPrefixedBuffer>()?.into(),
})),
ItemType::UserItem => Ok(Self::UserItem(UserItem {
bounds,
enabled,
unknown,
payload: reader.read_be::<LengthPrefixedBuffer>()?.into(),
})),
ItemType::HelpItem => Ok(Self::HelpItem(HelpItem {
bounds,
enabled,
unknown,
payload: reader.read_be::<LengthPrefixedBuffer>()?.into(),
})),
ItemType::CheckBox => Ok(Self::CheckBox(CheckBox {
bounds,
enabled,
unknown,
label: reader.read_be::<PascalString>()?.into(),
})),
ItemType::RadioButton => Ok(Self::RadioButton(RadioButton {
bounds,
enabled,
unknown,
label: reader.read_be::<PascalString>()?.into(),
})),
ItemType::Icon => Ok(Self::Icon(Icon {
bounds,
enabled,
unknown,
resource: reader.read_be::<ResourceRef>()?.into(),
})),
ItemType::Picture => Ok(Self::Picture(Picture {
bounds,
enabled,
unknown,
resource: reader.read_be::<ResourceRef>()?.into(),
})),
ItemType::Unknown(item_type) => Ok(Self::Unknown(UnknownDialogItem {
bounds,
enabled,
item_type,
unknown,
payload: reader.read_be::<LengthPrefixedBuffer>()?.into(),
})),
};
if reader.stream_position()? % 2 != 0 {
let _ = reader.seek(std::io::SeekFrom::Current(1))?;
}
result
}
}
#[binread]
#[derive(Resource, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[resource(code = "DITL")]
#[br(big)]
pub struct DialogItemList {
#[br(map(long_list_plus_one))]
items: Vec<DialogItem>,
}
impl From<DialogItemList> for Vec<DialogItem> {
fn from(val: DialogItemList) -> Self {
val.items
}
}
#[binread]
struct ResourceRef {
#[br(temp, assert(size==2))]
size: u8,
reference: u16,
}
impl From<ResourceRef> for u16 {
fn from(val: ResourceRef) -> Self {
val.reference
}
}
#[derive(Resource, BinRead)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[resource(code = "ictb")]
#[br(big)]
pub struct ItemColorTable(Clut);