1use crate::RelPtr64;
2use binrw::BinRead;
3use ssbh_write::SsbhWrite;
4use std::{io::Read, str::FromStr};
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8
9#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
11#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
12#[derive(Debug, BinRead, SsbhWrite, PartialEq, Eq, Clone)]
13pub struct SsbhStringN<const N: usize>(RelPtr64<CString<N>>);
14
15pub type SsbhString = SsbhStringN<4>;
17
18pub type SsbhString8 = SsbhStringN<8>;
20
21pub type CString1 = CString<1>;
23
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27#[derive(Debug, PartialEq, Eq, Clone)]
28pub struct CString<const N: usize>(
29 #[cfg_attr(
31 feature = "serde",
32 serde(
33 serialize_with = "serialize_str_bytes",
34 deserialize_with = "deserialize_str_bytes"
35 )
36 )]
37 Vec<u8>,
38);
39
40impl<const N: usize> CString<N> {
41 pub fn from_bytes(bytes: &[u8]) -> Self {
43 Self(bytes.iter().copied().take_while(|b| *b != 0u8).collect())
44 }
45
46 pub fn to_str(&self) -> Option<&str> {
49 std::str::from_utf8(&self.0).ok()
50 }
51
52 pub fn to_string_lossy(&self) -> String {
54 self.to_str().unwrap_or("").to_string()
55 }
56}
57
58impl<const N: usize> BinRead for CString<N> {
59 type Args<'a> = ();
60
61 fn read_options<R: Read + std::io::Seek>(
62 reader: &mut R,
63 _endian: binrw::Endian,
64 _args: Self::Args<'_>,
65 ) -> binrw::BinResult<Self> {
66 let mut bytes = Vec::new();
68 loop {
69 let b = u8::read(reader)?;
70 if b == 0 {
71 return Ok(Self(bytes));
72 }
73 bytes.push(b);
74 }
75 }
76}
77
78#[cfg(feature = "serde")]
79fn serialize_str_bytes<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
80where
81 S: Serializer,
82{
83 let text = CString::<1>::from_bytes(bytes).to_string_lossy();
84 serializer.serialize_str(&text)
85}
86
87#[cfg(feature = "serde")]
88fn deserialize_str_bytes<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
89where
90 D: Deserializer<'de>,
91{
92 let string = String::deserialize(deserializer)?;
93
94 Ok(string.as_bytes().to_vec())
96}
97
98#[cfg(feature = "arbitrary")]
99impl<'a, const N: usize> arbitrary::Arbitrary<'a> for CString<N> {
100 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
101 let bytes = Vec::<u8>::arbitrary(u)?;
102 Ok(Self::from_bytes(&bytes))
103 }
104}
105
106impl<const N: usize> FromStr for CString<N> {
107 type Err = core::convert::Infallible;
108
109 fn from_str(s: &str) -> Result<Self, Self::Err> {
110 Ok(s.into())
111 }
112}
113
114impl<const N: usize> From<&str> for CString<N> {
115 fn from(text: &str) -> Self {
116 Self::from_bytes(text.as_bytes())
117 }
118}
119
120impl<const N: usize> From<&String> for CString<N> {
121 fn from(text: &String) -> Self {
122 Self::from_bytes(text.as_bytes())
123 }
124}
125
126impl<const N: usize> From<String> for CString<N> {
127 fn from(text: String) -> Self {
128 Self::from_bytes(text.as_bytes())
129 }
130}
131
132impl<const N: usize> crate::SsbhWrite for CString<N> {
133 fn ssbh_write<W: std::io::Write + std::io::Seek>(
134 &self,
135 writer: &mut W,
136 _data_ptr: &mut u64,
137 ) -> std::io::Result<()> {
138 if self.0.is_empty() {
139 writer.write_all(&[0u8; N])?;
141 } else {
142 writer.write_all(&self.0)?;
144 writer.write_all(&[0u8])?;
145 }
146 Ok(())
147 }
148
149 fn size_in_bytes(&self) -> u64 {
150 self.0.size_in_bytes()
151 }
152
153 fn alignment_in_bytes() -> u64 {
154 N as u64
155 }
156}
157
158impl<const N: usize> SsbhStringN<N> {
159 pub fn from_bytes(bytes: &[u8]) -> Self {
161 Self(RelPtr64::new(CString::from_bytes(bytes)))
162 }
163
164 pub fn to_str(&self) -> Option<&str> {
167 self.0.as_ref()?.to_str()
168 }
169
170 pub fn to_string_lossy(&self) -> String {
173 self.to_str().unwrap_or("").to_string()
174 }
175}
176
177impl<const N: usize> FromStr for SsbhStringN<N> {
178 type Err = core::convert::Infallible;
179
180 fn from_str(s: &str) -> Result<Self, Self::Err> {
181 Ok(s.into())
182 }
183}
184
185impl<const N: usize> From<&str> for SsbhStringN<N> {
186 fn from(text: &str) -> Self {
187 Self::from_bytes(text.as_bytes())
188 }
189}
190
191impl<const N: usize> From<&String> for SsbhStringN<N> {
192 fn from(text: &String) -> Self {
193 Self::from_bytes(text.as_bytes())
194 }
195}
196
197impl<const N: usize> From<String> for SsbhStringN<N> {
198 fn from(text: String) -> Self {
199 Self::from_bytes(text.as_bytes())
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use binrw::io::Cursor;
206 use binrw::BinReaderExt;
207
208 use hexlit::hex;
209
210 use super::*;
211
212 #[test]
213 fn read_ssbh_string() {
214 let mut reader = Cursor::new(hex!(
215 "08000000 00000000 616C705F 6D617269 6F5F3030 325F636F 6C000000"
216 ));
217 let value = reader.read_le::<SsbhString>().unwrap();
218 assert_eq!("alp_mario_002_col", value.to_str().unwrap());
219
220 let value = reader.read_le::<u8>().unwrap();
222 assert_eq!(0x61u8, value);
223 }
224
225 #[test]
226 fn read_ssbh_string_empty() {
227 let mut reader = Cursor::new(hex!("08000000 00000000 00000000"));
228 let value = reader.read_le::<SsbhString>().unwrap();
229 assert_eq!("", value.to_str().unwrap());
230
231 let value = reader.read_le::<u8>().unwrap();
233 assert_eq!(0u8, value);
234 }
235
236 #[test]
237 fn read_ssbh_string_missing_null() {
238 let mut reader = Cursor::new(hex!("08000000 FFFFFFFF"));
240 let result = reader.read_le::<SsbhString>();
241 assert!(result.is_err());
242 }
243
244 #[test]
245 fn cstring_to_string_conversion() {
246 assert_eq!(Some("abc"), CString::<4>::from_bytes(b"abc\0").to_str());
247 assert_eq!(
248 "abc".to_string(),
249 CString::<4>::from_bytes(b"abc\0").to_string_lossy()
250 );
251 }
252
253 #[test]
254 fn ssbh_string_to_string_conversion() {
255 assert_eq!(Some("abc"), SsbhString::from("abc").to_str());
256 assert_eq!("abc".to_string(), SsbhString::from("abc").to_string_lossy());
257 }
258
259 #[test]
260 fn ssbh_string8_to_string_conversion() {
261 assert_eq!(Some("abc"), SsbhString8::from("abc").to_str());
262 assert_eq!(
263 "abc".to_string(),
264 SsbhString8::from("abc").to_string_lossy()
265 );
266 }
267
268 #[test]
269 fn ssbh_string_from_str() {
270 let s = SsbhString::from_str("abc").unwrap();
271 assert_eq!("abc", s.to_str().unwrap());
272 }
273
274 #[test]
275 fn ssbh_string8_from_str() {
276 let s = SsbhString8::from_str("abc").unwrap();
277 assert_eq!("abc", s.to_str().unwrap());
278 }
279
280 #[test]
281 fn ssbh_write_string() {
282 let value = SsbhString::from("scouter1Shape");
283
284 let mut writer = Cursor::new(Vec::new());
285 let mut data_ptr = 0;
286 value.ssbh_write(&mut writer, &mut data_ptr).unwrap();
287
288 assert_eq!(
289 writer.into_inner(),
290 hex!("08000000 00000000 73636F75 74657231 53686170 6500")
291 );
292 assert_eq!(24, data_ptr);
294 }
295
296 #[test]
297 fn ssbh_write_string_empty() {
298 let value = SsbhString::from("");
299
300 let mut writer = Cursor::new(Vec::new());
301 let mut data_ptr = 0;
302 value.ssbh_write(&mut writer, &mut data_ptr).unwrap();
303
304 assert_eq!(writer.into_inner(), hex!("08000000 00000000 00000000"));
305 assert_eq!(12, data_ptr);
307 }
308
309 #[test]
310 fn ssbh_write_string_non_zero_data_ptr() {
311 let value = SsbhString::from("scouter1Shape");
312
313 let mut writer = Cursor::new(Vec::new());
314 let mut data_ptr = 5;
315 value.ssbh_write(&mut writer, &mut data_ptr).unwrap();
316
317 assert_eq!(
318 writer.into_inner(),
319 hex!("08000000 00000000 73636F75 74657231 53686170 6500")
320 );
321 assert_eq!(24, data_ptr);
323 }
324
325 #[test]
326 fn ssbh_write_string_tuple() {
327 #[derive(SsbhWrite)]
328 struct StringPair {
329 item1: SsbhString,
330 item2: SsbhString,
331 }
332
333 let value = StringPair {
335 item1: SsbhString::from("RTV_FRAME_BUFFER_COPY"),
336 item2: SsbhString::from("FB_FRAME_BUFFER_COPY"),
337 };
338
339 let mut writer = Cursor::new(Vec::new());
340 let mut data_ptr = 0;
341 value.ssbh_write(&mut writer, &mut data_ptr).unwrap();
342
343 assert_eq!(
345 writer.into_inner(),
346 hex!(
347 "10000000 00000000 20000000 00000000
348 5254565F 4652414D 455F4255 46464552
349 5F434F50 59000000 46425F46 52414D45
350 5F425546 4645525F 434F5059 00"
351 )
352 );
353 }
354
355 #[test]
356 fn ssbh_write_string8() {
357 let value = SsbhString8::from("BlendState0");
358
359 let mut writer = Cursor::new(Vec::new());
360 let mut data_ptr = 0;
361 value.ssbh_write(&mut writer, &mut data_ptr).unwrap();
362
363 assert_eq!(
364 writer.into_inner(),
365 hex!("08000000 00000000 426C656E 64537461 74653000")
366 );
367 assert_eq!(24, data_ptr);
369 }
370
371 #[test]
372 fn ssbh_write_string8_empty() {
373 let value = SsbhString8::from("");
374
375 let mut writer = Cursor::new(Vec::new());
376 let mut data_ptr = 0;
377 value.ssbh_write(&mut writer, &mut data_ptr).unwrap();
378
379 assert_eq!(
380 writer.into_inner(),
381 hex!("08000000 00000000 00000000 00000000")
382 );
383 assert_eq!(16, data_ptr);
385 }
386
387 #[test]
388 fn ssbh_write_string8_non_zero_data_ptr() {
389 let value = SsbhString8::from("BlendState0");
390
391 let mut writer = Cursor::new(Vec::new());
392 let mut data_ptr = 5;
393 value.ssbh_write(&mut writer, &mut data_ptr).unwrap();
394
395 assert_eq!(
396 writer.into_inner(),
397 hex!("08000000 00000000 426C656E 64537461 74653000")
398 );
399 assert_eq!(24, data_ptr);
401 }
402}