/* wearechat - generic multiplayer game with voip Copyright (C) 2025 metamuffin This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License only. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ use crate::{helper::ReadWrite, packets::Resource}; use anyhow::Result; use glam::{Affine3A, Vec2, Vec3A, Vec4}; use log::warn; use std::{ borrow::Cow, collections::BTreeMap, io::{Read, Write}, }; macro_rules! rd_member_type { (multi, $t:ty) => { Vec<$t> }; (, $t:ty) => { Option<$t> }; } macro_rules! rd_member_read { (multi, $n:ident, $s:ident, $v:ident) => { Ok($s.$n.push(read_slice($v)?)) }; (, $n:ident, $s:ident, $v:ident) => { Ok($s.$n = Some(read_slice($v)?)) }; } macro_rules! rd_member_write { (multi, $n:ident, $s:ident, $w:ident) => { for x in &$s.$n { write_kv_opt($w, stringify!($n), &Some(x.clone()))?; } }; (, $n:ident, $s:ident, $w:ident) => { write_kv_opt($w, stringify!($n), &$s.$n)? }; } macro_rules! resource_dict { ($svis:vis struct $sname:ident { $($mname:ident$([$mflags:ident])?: $mty:ty),*, }) => { #[derive(Debug, Default, Clone)] $svis struct $sname { $(pub $mname: rd_member_type!($($mflags)?, $mty)),*, } impl ReadWrite for $sname { fn write(&self, w: &mut dyn Write) -> Result<()> { $(rd_member_write!($($mflags)?, $mname, self, w);)* Ok(()) } fn read(r: &mut dyn Read) -> Result { let mut s = Self::default(); read_kv_iter(r, |k, v| match k { $(stringify!($mname) => rd_member_read!($($mflags)?, $mname, s, v)),*, x => Ok(warn!("unknown {} key: {x:?}", stringify!($sname))), })?; Ok(s) } } }; } macro_rules! resource_dicts { ($(pub struct $name:ident $c:tt)*) => { $(resource_dict!(pub struct $name $c);)* }; } resource_dicts!( pub struct Prefab { name: String, mesh[multi]: (Affine3A, Resource), collision[multi]: (Affine3A, Resource), light[multi]: (Vec3A, Resource), armature[multi]: Resource, particles[multi]: (Affine3A, Resource), animation[multi]: Resource, avatar_info: Resource, environment: Resource, } pub struct LightPart { name: String, emission: Vec3A, radius: f32, } pub struct AnimationPart { name: String, channel[multi]: AnimationChannel, duration: f32, } pub struct AnimationChannel { t_mesh_translation: u32, t_mesh_rotation: u32, t_mesh_scale: u32, t_mesh_morph_weight: (u32, u32), t_joint_translation: (u32, u32), t_joint_rotation: (u32, u32), t_joint_scale: (u32, u32), t_light_translation: u32, time: Resource>, value: Resource>, } pub struct AvatarInfoPart { armature: u32, camera_mount: u32, camera_mount_offset: Vec3A, a_walk: u32, a_run: u32, a_sit: u32, } pub struct EnvironmentPart { skybox: Resource>, sun: (Vec3A, Vec3A), } pub struct ParticlesPart { sprite: Resource, density: f32, lifetime: f32, lifetime_spread: f32, velocity: Vec3A, velocity_spread: Vec3A, } pub struct MeshPart { name: String, index: Resource>, armature: u32, g_metallic: f32, g_roughness: f32, g_albedo: Vec3A, g_transmission: f32, g_alpha: f32, g_emission: Vec3A, g_thickness: f32, g_refractive_index: f32, g_attenuation: Vec3A, g_dispersion: f32, g_unlit: (), g_double_sided: (), va_position: Resource>, va_normal: Resource>, va_tangent: Resource>, va_texcoord: Resource>, va_roughness: Resource>, va_metallic: Resource>, va_albedo: Resource>, va_transmission: Resource>, va_alpha: Resource>, va_emission: Resource>, va_joint_index: Resource>, va_joint_weight: Resource>, tex_normal: Resource>, tex_roughness: Resource>, tex_metallic: Resource>, tex_albedo: Resource>, tex_transmission: Resource>, tex_alpha: Resource>, tex_emission: Resource>, tex_thickness: Resource>, tex_occlusion: Resource>, hint_mirror: (), hint_hide_first_person: (), hint_static: (), hint_volume: (), } pub struct ArmaturePart { parent: Vec, transform: Vec, name: Vec, } pub struct CollisionPart { name: String, restitution_coeff: f32, friction_kinetic: f32, friction_static: f32, sh_box: Vec3A, sh_sphere: f32, sh_cylinder: (f32, f32, f32), sh_capsule: (f32, f32, f32), sh_convex_hull: Resource>, sh_mesh: (Resource>, Resource>), } ); #[derive(Debug, Default, Clone)] pub struct PrefabIndex(pub BTreeMap>); #[derive(Debug, Clone)] pub struct Image<'a>(pub Cow<'a, [u8]>); impl ReadWrite for PrefabIndex { fn write(&self, w: &mut dyn Write) -> Result<()> { for (k, v) in &self.0 { write_kv(w, k, &v.0)?; } Ok(()) } fn read(r: &mut dyn Read) -> Result { let mut s = Self(BTreeMap::new()); let mut g = Vec::new(); r.read_to_end(&mut g)?; let mut g = g.as_slice(); while !g.is_empty() { let (k, v) = read_kv(&mut g)?; s.0.insert(k, Resource::read(&mut v.as_slice())?); } Ok(s) } } fn read_kv(r: &mut &[u8]) -> Result<(String, Vec)> { let mut key_size = [0; { size_of::() }]; let mut value_size = [0; { size_of::() }]; r.read_exact(&mut key_size)?; r.read_exact(&mut value_size)?; let key_size = u16::from_le_bytes(key_size); let value_size = u16::from_le_bytes(value_size); let mut key = vec![0; key_size as usize]; let mut value = vec![0; value_size as usize]; r.read_exact(&mut key)?; r.read_exact(&mut value)?; Ok((String::from_utf8_lossy_owned(key), value)) } fn read_kv_iter(r: &mut dyn Read, mut cb: impl FnMut(&str, &[u8]) -> Result<()>) -> Result<()> { let mut g = Vec::new(); r.read_to_end(&mut g)?; let mut g = g.as_slice(); while !g.is_empty() { let (k, v) = read_kv(&mut g)?; cb(&k, &v)? } Ok(()) } fn read_slice(mut r: &[u8]) -> Result { T::read(&mut r) } fn write_kv_opt(w: &mut dyn Write, key: &str, value: &Option) -> Result<()> { if let Some(v) = value { write_kv(w, key, &v.write_alloc())?; } Ok(()) } fn write_kv(w: &mut dyn Write, key: &str, value: &[u8]) -> Result<()> { w.write_all(&(key.len() as u16).to_le_bytes())?; w.write_all(&(value.len() as u16).to_le_bytes())?; w.write_all(key.as_bytes())?; w.write_all(value)?; Ok(()) } impl ReadWrite for Image<'_> { fn write(&self, w: &mut dyn Write) -> Result<()> { self.0.write(w) } fn read(r: &mut dyn Read) -> Result { Ok(Self( as ReadWrite>::read(r)?.into())) } fn write_alloc(&self) -> Cow<'_, [u8]> { self.0.write_alloc() } }