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
use byteorder::{ReadBytesExt, BE};
use std::io::{Cursor, Read};
use tracing::trace;
pub(crate) const LFO_RESP_HDR_LEN: usize = 0x2A;
pub(crate) const CRC_LEN: usize = 4;
#[repr(u16)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum CompressionFormats {
None = 0,
Xz = 1,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct LfoFileHeader {
pub magic: u32,
pub unk_cst1: u16,
pub comp_format: u16,
pub payload_size: u32,
pub data_hash: [u8; 32],
pub cur_payload_size: u32,
pub cur_state: u16,
pub unk: u16,
}
impl TryFrom<&[u8]> for LfoFileHeader {
type Error = String;
fn try_from(lfo_payload: &[u8]) -> Result<Self, Self::Error> {
if lfo_payload.len() < LFO_RESP_HDR_LEN + CRC_LEN {
return Err("LFO OK header too small".into());
}
let header = &lfo_payload[..LFO_RESP_HDR_LEN];
let payload_data = &lfo_payload[LFO_RESP_HDR_LEN..]; let mut header_reader = Cursor::new(&header);
let chunk_start_off = header_reader.read_u32::<BE>().unwrap();
let chunk_end_off = header_reader.read_u32::<BE>().unwrap();
let mut pkt_unk_buf = [0; 32];
header_reader.read_exact(&mut pkt_unk_buf).unwrap();
let comp_format = header_reader.read_u16::<BE>().unwrap();
trace!("Received LFO header data: {}", hex::encode(header));
if chunk_start_off > chunk_end_off {
return Err(format!(
"LFO response start offset {:#x} is past end offset {:#x}",
chunk_start_off, chunk_end_off
));
}
let len_without_crc = payload_data.len() - CRC_LEN;
if chunk_start_off != 0 {
return Err("Unexpected non-0 offset in LFO response".into());
}
let chunk_size = chunk_end_off - chunk_start_off;
if comp_format == 0 && chunk_size != len_without_crc as u32 {
return Err(format!(
"Expected {:#x} bytes LFO file data, but uncompressed payload is {:#x} bytes",
chunk_size, len_without_crc
));
}
let expected_crc = u32::from_be_bytes(payload_data[len_without_crc..].try_into().unwrap());
let crc = crc32fast::hash(&payload_data[..len_without_crc]);
if crc != expected_crc {
return Err(format!(
"Expected CRC 0x{:X}, but computed 0x{:X}",
expected_crc, crc
));
}
Ok(Self {
magic: 0x4C444852, unk_cst1: 1,
comp_format,
payload_size: chunk_end_off,
data_hash: pkt_unk_buf,
cur_payload_size: len_without_crc as u32,
cur_state: 5,
unk: 0,
})
}
}