pub(crate) mod builder;
mod chunk;
mod lists;
mod numbers;
mod pointers;
mod resources;
mod versioning;
use std::path::Path;
use builder::DataBuilder;
use crate::prelude::*;
use crate::util::bench::Stopwatch;
use crate::util::panic;
use crate::wad::chunk::ChunkName;
use crate::wad::data::GMData;
use crate::wad::elements::string::GMStrings;
#[inline]
pub fn build_bytes(gm_data: &GMData) -> Result<Vec<u8>> {
build(gm_data).context("building GameMaker data bytes")
}
pub fn build_file(gm_data: &GMData, path: impl AsRef<Path>) -> Result<()> {
let path: &Path = path.as_ref();
let raw_data: Vec<u8> = build(gm_data)
.with_context(|| format!("building GameMaker data file {}", path.display()))?;
let stopwatch = Stopwatch::start();
std::fs::write(path, raw_data).context_src("writing data file")?;
log::trace!("Writing data file took {stopwatch}");
Ok(())
}
#[inline]
fn build(gm_data: &GMData) -> Result<Vec<u8>> {
if cfg!(feature = "catch-panic") {
panic::catch(|| build_impl(gm_data))
} else {
build_impl(gm_data)
}
}
fn build_impl(data: &GMData) -> Result<Vec<u8>> {
let stopwatch = Stopwatch::start();
let mut builder = DataBuilder::new(data);
builder.write_chunk_name(ChunkName::new("FORM"));
builder.write_u32(0xDEAD_C0DE);
builder.build_chunk(&data.general_info)?;
builder.build_chunk(&data.options)?;
builder.build_chunk(&data.extensions)?;
builder.build_chunk(&data.sounds)?;
builder.build_chunk(&data.audio_groups)?;
builder.build_chunk(&data.sprites)?;
builder.build_chunk(&data.backgrounds)?;
builder.build_chunk(&data.paths)?;
builder.build_chunk(&data.scripts)?;
builder.build_chunk(&data.shaders)?;
builder.build_chunk(&data.fonts)?;
builder.build_chunk(&data.timelines)?;
builder.build_chunk(&data.game_objects)?;
builder.build_chunk(&data.rooms)?;
builder.build_chunk(&data.texture_page_items)?;
builder.build_chunk(&data.codes)?;
builder.build_chunk(&data.variables)?;
builder.build_chunk(&data.functions)?;
builder.build_chunk(&data.texture_pages)?;
builder.build_chunk(&data.audios)?;
builder.build_chunk(&data.sequences)?;
builder.build_chunk(&data.particle_systems)?;
builder.build_chunk(&data.particle_emitters)?;
builder.build_chunk(&data.language_info)?;
builder.build_chunk(&data.global_init_scripts)?;
builder.build_chunk(&data.game_end_scripts)?;
builder.build_chunk(&data.ui_nodes)?;
builder.build_chunk(&data.embedded_images)?;
builder.build_chunk(&data.texture_group_infos)?;
builder.build_chunk(&data.tags)?;
builder.build_chunk(&data.feature_flags)?;
builder.build_chunk(&data.filter_effects)?;
builder.build_chunk(&data.animation_curves)?;
builder.build_chunk(&GMStrings)?;
builder.remove_last_chunk_padding();
builder.connect_pointer_placeholders()?;
builder.overwrite_u32(builder.len() - 8, 4)?;
log::trace!("Building data file took {stopwatch}");
let raw_data: Vec<u8> = builder.finish();
if raw_data.len() >= i32::MAX as usize {
bail!("Data file is bigger than 2,147,483,646 bytes which will lead to bugs in the runner")
}
Ok(raw_data)
}