pub mod keyframe;
pub use keyframe::Keyframe;
pub use keyframe::Keyframes;
use crate::gm_enum::gm_enum;
use crate::prelude::*;
use crate::util::init::vec_with_capacity;
use crate::wad::build::builder::DataBuilder;
use crate::wad::elem::GMElement;
use crate::wad::elem::animation_curve::AnimationCurve;
use crate::wad::parse::reader::DataReader;
#[derive(Debug, Clone, PartialEq)]
pub struct Track {
pub model_name: GMRef<String>,
pub name: GMRef<String>,
pub builtin_name: BuiltinName,
pub flags: Flags,
pub is_creation_track: bool,
pub tags: Vec<i32>,
pub sub_tracks: Vec<Self>,
pub keyframes: Keyframes,
pub owned_resources: Vec<AnimationCurve>,
animcurve_string: GMRef<String>,
}
impl GMElement for Track {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
let model_name_str: &str = force_read_string(reader)?;
reader.cur_pos -= 4;
let model_name: GMRef<String> = reader.read_gm_string()?;
let name: GMRef<String> = reader.read_gm_string()?;
let builtin_name: BuiltinName = reader.read_enum()?;
let flags = reader.read_i32()?;
let flags =
Flags::from_bits(flags).ok_or_else(|| format!("Invalid Track Flags {flags:08X}"))?;
let is_creation_track = reader.read_bool32()?;
let tag_count = reader.read_count("Track Tag")?;
let owned_resources_count = reader.read_count("Track Owned Resource")?;
let track_count = reader.read_count("Track")?;
let mut tags: Vec<i32> = vec_with_capacity(tag_count)?;
for _ in 0..tag_count {
tags.push(reader.read_i32()?);
}
let mut owned_resources: Vec<AnimationCurve> = vec_with_capacity(owned_resources_count)?;
let mut animcurve_string = GMRef::none();
for _ in 0..owned_resources_count {
let string: &str = force_read_string(reader)?;
reader.cur_pos -= 4;
animcurve_string = reader.read_gm_string()?;
if string != "GMAnimCurve" {
bail!(
"Expected owned resource thingy of Track to be \"GMAnimCurve\"; but found \
{:?} for Track {:?}",
animcurve_string,
name,
);
}
owned_resources.push(AnimationCurve::deserialize(reader)?);
}
let mut sub_tracks: Vec<Self> = vec_with_capacity(track_count)?;
for _ in 0..track_count {
sub_tracks.push(Self::deserialize(reader)?);
}
let keyframes = match model_name_str {
"GMAudioTrack" => Keyframes::Audio(keyframe::Data::deserialize(reader)?),
"GMInstanceTrack" => Keyframes::Instance(keyframe::Data::deserialize(reader)?),
"GMGraphicTrack" => Keyframes::Graphic(keyframe::Data::deserialize(reader)?),
"GMSequenceTrack" => Keyframes::Sequence(keyframe::Data::deserialize(reader)?),
"GMSpriteFramesTrack" => Keyframes::SpriteFrames(keyframe::Data::deserialize(reader)?),
"GMAssetTrack" => bail!("Asset Track not yet supported"),
"GMBoolTrack" => Keyframes::Bool(keyframe::Data::deserialize(reader)?),
"GMStringTrack" => Keyframes::String(keyframe::Data::deserialize(reader)?),
"GMIntTrack" => bail!("Int Track not yet supported"),
"GMColourTrack" => {
Keyframes::Color(keyframe::color::KeyframesData::deserialize(reader)?)
}
"GMRealTrack" => Keyframes::Real(keyframe::color::KeyframesData::deserialize(reader)?),
"GMTextTrack" => Keyframes::Text(keyframe::Data::deserialize(reader)?),
"GMParticleTrack" => Keyframes::Particle(keyframe::Data::deserialize(reader)?),
_ => bail!("Invalid Track Model Name {model_name:?}"),
};
Ok(Self {
model_name,
name,
builtin_name,
flags,
is_creation_track,
tags,
sub_tracks,
keyframes,
owned_resources,
animcurve_string,
})
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_gm_string(self.model_name)?;
builder.write_gm_string(self.name)?;
builder.write_enum(self.builtin_name);
builder.write_i32(self.flags.bits());
builder.write_bool32(self.is_creation_track);
builder.write_usize(self.tags.len())?;
builder.write_usize(self.owned_resources.len())?;
builder.write_usize(self.sub_tracks.len())?;
for tag in &self.tags {
builder.write_i32(*tag);
}
for animation_curve in &self.owned_resources {
builder.write_gm_string(self.animcurve_string)?;
animation_curve.serialize(builder)?;
}
for track in &self.sub_tracks {
track.serialize(builder)?;
}
match &self.keyframes {
Keyframes::Audio(k) => k.serialize(builder)?,
Keyframes::Instance(k) => k.serialize(builder)?,
Keyframes::Graphic(k) => k.serialize(builder)?,
Keyframes::Sequence(k) => k.serialize(builder)?,
Keyframes::SpriteFrames(k) => k.serialize(builder)?,
Keyframes::Bool(k) => k.serialize(builder)?,
Keyframes::String(k) => k.serialize(builder)?,
Keyframes::Color(k) | Keyframes::Real(k) => k.serialize(builder)?,
Keyframes::Text(k) => k.serialize(builder)?,
Keyframes::Particle(k) => k.serialize(builder)?,
Keyframes::BroadcastMessage(k) => k.serialize(builder)?,
}
Ok(())
}
}
gm_enum!( BuiltinName {
None = 0,
Gain = 5,
Pitch = 6,
Falloff = 7,
RotationOrImageAngle = 8,
BlendAdd = 9,
BlendMultiplyOrImageBlend = 10,
Mask = 12,
Subject = 13,
Position = 14,
Scale = 15,
Origin = 16,
ImageSpeed = 17,
ImageIndex = 18,
FrameSize = 20,
CharacterSpacing = 21,
LineSpacing = 22,
ParagraphSpacing = 23,
});
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Flags: i32 {
const CHILDREN_IGNORE_ORIGIN = 0x1;
}
}
fn force_read_string<'a>(reader: &mut DataReader<'a>) -> Result<&'a str> {
let string_pos = reader.read_u32()?;
if string_pos == 0 {
return Ok("");
}
let chunk = reader.chunk;
let pos = reader.cur_pos;
reader.chunk = reader.string_chunk;
reader.cur_pos = string_pos - 4;
let len = reader.read_u32().ctx("force-reading string length")?;
let bytes = reader.read_bytes_dyn(len).ctx("force-reading string")?;
let string = str::from_utf8(bytes).ctx_any("force-reading string")?;
reader.chunk = chunk;
reader.cur_pos = pos;
Ok(string)
}