1use std::io::{BufRead, Read};
2
3use sga_macros::read_field;
4use thiserror::Error;
5
6#[derive(Error, Debug)]
7pub enum SgaFileEntryParseError {
8 #[error("Failed to parse number `{0}`")]
9 FailedToParseNumber(String),
10 #[error("Failed to read byte value `{0}`")]
11 FailedToParseByte(String),
12 #[error("Failed to parse storage type `{0}`")]
13 FailedToParseStorageType(String),
14 #[error("Failed to parse verification type `{0}`")]
15 FailedToParseVerificationType(String),
16}
17
18#[derive(Debug, Clone)]
20pub enum FileVerificationType {
21 None,
23
24 CRC,
26
27 CRCBlocks,
29
30 MD5Blocks,
32
33 SHA1Blocks,
35}
36
37impl FileVerificationType {
38 pub fn from_u8(value: u8) -> Result<Self, String> {
40 match value {
41 0 => Ok(Self::None),
42 1 => Ok(Self::CRC),
43 2 => Ok(Self::CRCBlocks),
44 3 => Ok(Self::MD5Blocks),
45 4 => Ok(Self::SHA1Blocks),
46 _ => Err("Invalid file verification type".into()),
47 }
48 }
49
50 pub fn to_u8(self) -> u8 {
52 match self {
53 FileVerificationType::None => 0,
54 FileVerificationType::CRC => 1,
55 FileVerificationType::CRCBlocks => 2,
56 FileVerificationType::MD5Blocks => 3,
57 FileVerificationType::SHA1Blocks => 4,
58 }
59 }
60}
61
62#[derive(Debug, Clone)]
64pub enum FileStorageType {
65 Store,
67
68 StreamCompress,
70
71 BufferCompress,
73
74 StreamCompressBrotli,
76
77 BufferCompressBrotli,
79
80 Unknown(u8),
82}
83
84impl FileStorageType {
85 pub fn from_u8(value: u8) -> Self {
87 match value {
88 0 => Self::Store,
89 1 => Self::StreamCompress,
90 2 => Self::BufferCompress,
91 3 => Self::StreamCompressBrotli,
92 4 => Self::BufferCompressBrotli,
93 _ => Self::Unknown(value),
94 }
95 }
96
97 pub fn to_u8(self) -> u8 {
98 match self {
99 FileStorageType::Store => 0,
100 FileStorageType::StreamCompress => 1,
101 FileStorageType::BufferCompress => 2,
102 FileStorageType::StreamCompressBrotli => 3,
103 FileStorageType::BufferCompressBrotli => 4,
104 FileStorageType::Unknown(n) => n,
105 }
106 }
107}
108
109#[derive(Debug, Clone)]
111pub struct SgaFileEntry {
112 pub name_offset: u32,
114
115 pub hash_offset: u32,
117
118 pub data_offset: u64,
120
121 pub compressed_length: u32,
123
124 pub uncompressed_size: u32,
126
127 pub verification_type: FileVerificationType,
129
130 pub storage_type: FileStorageType,
132
133 pub crc: u32,
135}
136
137impl SgaFileEntry {
138 pub fn parse<T: Read + BufRead>(reader: &mut T) -> Result<Self, SgaFileEntryParseError> {
139 let name_offset = read_field!(reader, SgaFileEntryParseError::FailedToParseNumber, u32)?;
140 let hash_offset = read_field!(reader, SgaFileEntryParseError::FailedToParseNumber, u32)?;
141 let data_offset = read_field!(reader, SgaFileEntryParseError::FailedToParseNumber, u64)?;
142 let compressed_length = read_field!(reader, SgaFileEntryParseError::FailedToParseNumber, u32)?;
143 let uncompressed_size = read_field!(reader, SgaFileEntryParseError::FailedToParseNumber, u32)?;
144 let verification_type_byte = read_field!(reader, SgaFileEntryParseError::FailedToParseByte, u8)?;
145 let storage_type_byte = read_field!(reader, SgaFileEntryParseError::FailedToParseByte, u8)?;
146 let crc = read_field!(reader, SgaFileEntryParseError::FailedToParseNumber, u32)?;
147
148
149 let verification_type = FileVerificationType::from_u8(verification_type_byte).map_err(|err| SgaFileEntryParseError::FailedToParseVerificationType(err.to_string()))?;
150 let storage_type = FileStorageType::from_u8(storage_type_byte);
151
152 Ok(Self {
153 name_offset,
154 hash_offset,
155 data_offset,
156 compressed_length,
157 uncompressed_size,
158 verification_type,
159 storage_type,
160 crc,
161 })
162 }
163}