use std::collections::HashMap;
use crate::prelude::*;
use crate::util::fmt::typename;
use crate::wad::chunk::ChunkName;
use crate::wad::deserialize::reader::DataReader;
use crate::wad::reference::GMRef;
use crate::wad::serialize::builder::DataBuilder;
pub mod animation_curve;
pub mod audio;
pub mod audio_group;
pub mod background;
pub mod code;
pub(crate) mod data_file;
pub mod embedded_image;
pub mod extension;
pub mod feature_flag;
pub mod filter_effect;
pub mod font;
pub mod function;
pub mod game_end;
pub mod game_object;
pub mod general_info;
pub mod global_init;
pub mod language;
pub mod options;
pub mod particle_emitter;
pub mod particle_system;
pub mod path;
pub mod room;
pub mod script;
pub mod sequence;
pub mod shader;
pub mod sound;
pub mod sprite;
pub(crate) mod string;
pub mod tag;
pub mod texture_group_info;
pub mod texture_page;
pub mod texture_page_item;
pub mod timeline;
pub mod ui_node;
pub mod variable;
#[allow(unused_variables)]
pub trait GMElement: Sized {
#[doc(hidden)]
fn deserialize(reader: &mut DataReader) -> Result<Self>;
#[doc(hidden)]
fn serialize(&self, builder: &mut DataBuilder) -> Result<()>;
#[doc(hidden)]
fn deserialize_pre_padding(reader: &mut DataReader) -> Result<()> {
Ok(())
}
#[doc(hidden)]
fn serialize_pre_padding(&self, builder: &mut DataBuilder) -> Result<()> {
Ok(())
}
#[doc(hidden)]
fn deserialize_post_padding(reader: &mut DataReader, is_last: bool) -> Result<()> {
Ok(())
}
#[doc(hidden)]
fn serialize_post_padding(&self, builder: &mut DataBuilder, is_last: bool) -> Result<()> {
Ok(())
}
}
impl GMElement for u8 {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_u8()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_u8(*self);
Ok(())
}
}
impl GMElement for i8 {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_i8()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_i8(*self);
Ok(())
}
}
impl GMElement for u16 {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_u16()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_u16(*self);
Ok(())
}
}
impl GMElement for i16 {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_i16()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_i16(*self);
Ok(())
}
}
impl GMElement for u32 {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_u32()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_u32(*self);
Ok(())
}
}
impl GMElement for i32 {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_i32()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_i32(*self);
Ok(())
}
}
impl GMElement for u64 {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_u64()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_u64(*self);
Ok(())
}
}
impl GMElement for i64 {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_i64()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_i64(*self);
Ok(())
}
}
impl GMElement for f32 {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_f32()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_f32(*self);
Ok(())
}
}
impl GMElement for f64 {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_f64()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_f64(*self);
Ok(())
}
}
impl GMElement for bool {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_bool32()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_bool32(*self);
Ok(())
}
}
impl GMElement for String {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_gm_string()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_gm_string(self);
Ok(())
}
}
impl GMElement for Option<String> {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_gm_string_opt()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_gm_string_opt(self);
Ok(())
}
}
impl<T> GMElement for GMRef<T> {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_resource_by_id()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_resource_id(*self);
Ok(())
}
}
impl<T> GMElement for Option<GMRef<T>> {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
reader.read_resource_by_id_opt()
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_resource_id_opt(*self);
Ok(())
}
}
pub trait GMChunk: GMElement + Default {
const NAME: ChunkName;
fn exists(&self) -> bool;
}
pub trait GMListChunk: GMChunk {
type Element: GMElement;
#[must_use]
fn elements(&self) -> &Vec<Self::Element>;
#[must_use]
fn elements_mut(&mut self) -> &mut Vec<Self::Element>;
fn by_ref(&self, gm_ref: GMRef<Self::Element>) -> Result<&Self::Element> {
gm_ref.resolve(self.elements())
}
fn by_ref_mut(&mut self, gm_ref: GMRef<Self::Element>) -> Result<&mut Self::Element> {
gm_ref.resolve_mut(self.elements_mut())
}
fn push(&mut self, element: Self::Element) {
self.elements_mut().push(element);
}
#[must_use]
fn len(&self) -> usize {
self.elements().len()
}
#[must_use]
fn is_empty(&self) -> bool {
self.len() == 0
}
fn iter(&self) -> core::slice::Iter<'_, Self::Element>;
fn iter_mut(&mut self) -> core::slice::IterMut<'_, Self::Element>;
#[must_use]
fn into_iter(self) -> std::vec::IntoIter<Self::Element>;
}
pub trait GMNamedListChunk: GMListChunk<Element: GMNamedElement> {
fn ref_by_name(&self, name: &str) -> Result<GMRef<Self::Element>>;
fn by_name(&self, name: &str) -> Result<&Self::Element>;
fn by_name_mut(&mut self, name: &str) -> Result<&mut Self::Element>;
}
pub fn validate_names<T: GMNamedListChunk>(chunk: &T) -> Result<()> {
let elements = chunk.elements();
let mut seen: HashMap<&String, usize> = HashMap::new();
for (i, item) in elements.iter().enumerate() {
let name = item.name();
item.validate_name().with_context(|| {
format!(
"validating {} with name {:?}",
typename::<T::Element>(),
name,
)
})?;
let Some(first_index) = seen.insert(name, i) else {
continue;
};
bail!(
"There are multiple {} with the same name ({:?}): First at index {} and now at index \
{}",
typename::<T::Element>(),
name,
first_index,
i,
);
}
Ok(())
}
#[allow(unused_variables)]
pub trait GMNamedElement: GMElement {
#[must_use]
fn name(&self) -> &String;
#[must_use]
fn name_mut(&mut self) -> &mut String;
fn validate_name(&self) -> Result<()> {
validate_identifier(self.name())
}
}
pub(crate) fn validate_identifier(name: &str) -> Result<()> {
let first_char = name.chars().next().ok_or("Identifier is empty")?;
if first_char.is_ascii_digit() {
bail!("Identifier {name:?} starts with a digit ({first_char})");
}
for ch in name.chars() {
if !matches!(ch, 'a'..='z'| '0'..='9' | '_' | 'A'..='Z' | '@') {
bail!("Identifier {name:?} contains invalid character {ch:?}");
}
}
Ok(())
}
macro_rules! element_stub {
($type:ty) => {
impl $crate::wad::elements::GMElement for $type {
fn deserialize(_: &mut $crate::wad::deserialize::reader::DataReader) -> Result<Self> {
unimplemented!(
"Using {0}::deserialize is not supported, use {0}s::deserialize instead",
stringify!($type),
);
}
fn serialize(
&self,
_: &mut $crate::wad::serialize::builder::DataBuilder,
) -> Result<()> {
unimplemented!(
"Using {0}::serialize is not supported, use {0}s::serialize instead",
stringify!($type),
);
}
}
};
}
pub(crate) use element_stub;