/*
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()
}
}