1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
/*
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 <https://www.gnu.org/licenses/>.
*/
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<Resource<RespackEntry>>,
) -> 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::<u64>() + (32 + size_of::<u64>() * 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<Option<Resource<RespackEntry>>> {
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::<u64>()];
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::<u64>()];
let mut size = [0u8; size_of::<u64>()];
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)
}
|