physis/
svb.rs

1// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::io::Cursor;
5
6use crate::ByteBuffer;
7use crate::ByteSpan;
8use binrw::BinRead;
9use binrw::BinWrite;
10use binrw::binrw;
11
12#[binrw]
13#[derive(Debug)]
14#[brw(little)]
15#[brw(magic = b"SVB1")]
16pub struct Svb {
17    /// Including this header
18    pub file_size: u32,
19    /// Number of Svc's
20    #[br(temp)]
21    #[bw(calc = svcs.len() as u32)]
22    svc_count: u32,
23    #[br(count = svc_count)]
24    pub svcs: Vec<Svc>,
25}
26
27#[binrw]
28#[derive(Debug)]
29#[brw(little)]
30#[brw(magic = b"SVC1")]
31pub struct Svc {
32    #[br(temp)]
33    #[bw(calc = entries.len() as u32 + 1)]
34    pub num_entries: u32,
35    #[brw(pad_before = 4)] // empty?
36    pub unk1: u32,
37    pub unk2: u32,
38    // TODO: figure out what this is
39    // TODO: why is it -1?
40    #[br(count = num_entries - 1)]
41    pub entries: Vec<SvcEntry>,
42}
43
44#[binrw]
45#[derive(Debug)]
46#[brw(little)]
47pub struct SvcEntry {
48    // TODO: figure out what this is
49    #[br(count = 48)]
50    pub unk1: Vec<u8>,
51}
52
53impl Svb {
54    /// Reads an existing UWB file
55    pub fn from_existing(buffer: ByteSpan) -> Option<Self> {
56        let mut cursor = Cursor::new(buffer);
57        Svb::read(&mut cursor).ok()
58    }
59
60    pub fn write_to_buffer(&self) -> Option<ByteBuffer> {
61        let mut buffer = ByteBuffer::new();
62
63        {
64            let mut cursor = Cursor::new(&mut buffer);
65            self.write_le(&mut cursor).ok()?;
66        }
67
68        Some(buffer)
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use std::fs::read;
75    use std::path::PathBuf;
76
77    use super::*;
78
79    #[test]
80    fn test_invalid() {
81        let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
82        d.push("resources/tests");
83        d.push("random");
84
85        // Feeding it invalid data should not panic
86        Svb::from_existing(&read(d).unwrap());
87    }
88}