/* 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::{packets::Resource, resources::RespackEntry, store::ResourceStore}; use anyhow::{Result, bail}; use log::{info, warn}; use std::{ io::{Read, Seek, SeekFrom, Write}, marker::PhantomData, }; const MAGIC: &[u8; 16] = b"\x0f\x0cWEARE\x01RESPACK\x02"; pub fn save_respack( mut output: impl Write, store: &ResourceStore, resources: &[Resource], entry: Option>, ) -> Result<()> { output.write_all(MAGIC)?; output.write_all(&entry.map(|e| e.0).unwrap_or([0u8; 32]))?; output.write_all(&u64::to_be_bytes(resources.len() as u64))?; let mut off = (MAGIC.len() + 32 + size_of::() + (32 + size_of::() * 2) * resources.len()) as u64; for r in resources { let size = store.get_raw_size(*r)?.unwrap() as u64; output.write_all(&r.0)?; output.write_all(&u64::to_be_bytes(off))?; output.write_all(&u64::to_be_bytes(size))?; off += size; } for r in resources { output.write_all(&store.get_raw(*r)?.unwrap())?; } Ok(()) } pub fn load_respack( mut input: impl Read + Seek, store: &ResourceStore, ) -> Result>> { let mut magic = [0u8; MAGIC.len()]; input.read_exact(&mut magic)?; if magic != *MAGIC { bail!("wrong magic bytes"); } let mut entry = [0u8; 32]; input.read_exact(&mut entry)?; let entry = if entry != [0u8; 32] { Some(Resource(entry, PhantomData)) } else { None }; let mut count = [0u8; size_of::()]; input.read_exact(&mut count)?; let count = u64::from_be_bytes(count); let mut load_queue = Vec::new(); let mut found_entry = false; for _ in 0..count { let mut res = [0u8; 32]; let mut off = [0u8; size_of::()]; let mut size = [0u8; size_of::()]; input.read_exact(&mut res)?; input.read_exact(&mut off)?; input.read_exact(&mut size)?; found_entry |= Some(Resource(res, PhantomData)) == entry; if store.get_raw_size(Resource(res, PhantomData))?.is_none() { load_queue.push((res, u64::from_be_bytes(off), u64::from_be_bytes(size))) } } if !found_entry && entry.is_some() { warn!("respack does not contain its entry resource") } info!( "loading {} of {count} resources from pack", load_queue.len(), ); for (res, off, size) in load_queue { input.seek(SeekFrom::Start(off))?; let mut buf = Vec::new(); input.by_ref().take(size).read_to_end(&mut buf)?; let key = store.set_raw(&buf)?; if key.0 != res { warn!("respack containes mislabeled resources") } } Ok(entry) }