mod file;
pub mod function;
pub mod option;
pub use self::file::File;
pub use self::function::Function;
pub use self::option::ExtOption;
use crate::gm_enum::gm_enum;
use crate::prelude::*;
use crate::wad::build::builder::DataBuilder;
use crate::wad::chunk::gm_named_list_chunk;
use crate::wad::elem::GMElement;
use crate::wad::parse::reader::DataReader;
use crate::wad::version::GMVersion;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Extensions {
pub elems: Vec<Extension>,
pub exists: bool,
}
gm_named_list_chunk!(EXTN, Extensions, Extension, direct);
impl GMElement for Extensions {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
let mut elems: Vec<Extension> = reader.read_pointer_list()?;
if product_id_data_eligible(reader.general_info.version) {
for elem in &mut elems {
let bytes: [u8; 16] = reader.read_bytes_const()?.to_owned();
elem.product_id_data = Some(bytes);
}
}
Ok(Self { elems, exists: true })
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_pointer_list(&self.elems)?;
if !product_id_data_eligible(builder.version()) {
return Ok(());
}
for elem in &self.elems {
let Some(data) = &elem.product_id_data else {
bail!(
"Extension's Product ID Data is not set in {}",
builder.version()
);
};
builder.write_bytes(data);
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Extension {
pub folder_name: GMRef<String>,
pub name: GMRef<String>,
pub version: GMRef<String>,
pub class_name: GMRef<String>,
pub files: Vec<File>,
pub options: Vec<ExtOption>,
pub product_id_data: Option<[u8; 16]>,
}
impl GMElement for Extension {
fn deserialize(reader: &mut DataReader) -> Result<Self> {
let folder_name: GMRef<String> = reader.read_gm_string()?;
let name: GMRef<String> = reader.read_gm_string()?;
let version: GMRef<String> = if reader.general_info.version >= (2023, 4) {
reader.read_gm_string()?
} else {
GMRef::none()
};
let class_name: GMRef<String> = reader.read_gm_string()?;
let files: Vec<File>;
let options: Vec<ExtOption>;
if reader.general_info.version >= (2022, 6) {
let files_ptr = reader.read_u32()?;
let options_ptr = reader.read_u32()?;
reader.assert_pos(files_ptr, "Files")?;
files = reader.read_pointer_list()?;
reader.assert_pos(options_ptr, "Options")?;
options = reader.read_pointer_list()?;
} else {
files = reader.read_pointer_list()?;
options = Vec::new();
}
Ok(Self {
folder_name,
name,
version,
class_name,
files,
options,
product_id_data: None,
})
}
fn serialize(&self, builder: &mut DataBuilder) -> Result<()> {
builder.write_gm_string(self.folder_name)?;
builder.write_gm_string(self.name)?;
if builder.version() >= (2023, 4) {
builder.write_gm_string(self.version)?;
}
builder.write_gm_string(self.class_name)?;
if builder.version() >= (2022, 6) {
builder.write_pointer(&self.files);
builder.write_pointer(&self.options);
builder.resolve_pointer(&self.files)?;
builder.write_pointer_list(&self.files)?;
builder.resolve_pointer(&self.options)?;
builder.write_pointer_list(&self.options)?;
} else {
builder.write_pointer_list(&self.files)?;
}
Ok(())
}
}
gm_enum!( Kind {
Dll = 1,
Gml = 2,
ActionLib = 3,
Generic = 4,
JavaScript = 5,
Unknown1 = 11,
Unknown2 = 12,
});
#[must_use]
const fn product_id_data_eligible(ver: GMVersion) -> bool {
ver.major >= 2 || (ver.major == 1 && ver.build >= 1773) || (ver.major == 1 && ver.build == 1539)
}