1use std::path::Path;
2
3use utf16string::{LittleEndian, WString};
4
5use crate::{error, Format};
6
7const BOM_LE: &str = "\u{feff}";
8const BOM_BE: &str = "\u{ffef}";
9pub const SZ_INVALID_CHARS: &[char] = &['\n', '\r'];
10
11pub fn utf16_bytes_to_str(bytes: Vec<u8>) -> Option<String> {
12 let ws = WString::<LittleEndian>::from_utf16(bytes).ok()?;
13 Some(ws.to_utf8().trim_end_matches('\0').to_string())
14}
15
16pub fn ascii_bytes_to_str(bytes: Vec<u8>) -> Option<String> {
17 Some(String::from_utf8(bytes).ok()?.trim_end_matches('\0').to_string())
18}
19
20pub fn str_to_utf16_bytes(data: &str) -> Vec<u8> {
21 let ws = WString::<LittleEndian>::from(data.trim_end_matches('\0'));
22 let mut bytes = ws.into_bytes();
23 bytes.push(0);
24 bytes.push(0);
25 bytes
26}
27
28pub fn str_to_ascii_bytes(data: &str) -> Vec<u8> {
29 let mut bytes: Vec<_> = data.trim_end_matches('\0').chars().map(|c| c as u8).collect();
30 bytes.push(0);
31 bytes
32}
33
34pub fn read_bytes(bytes: Vec<u8>) -> Option<String> {
35 if let Ok(raw) = String::from_utf8(bytes.clone()) {
36 return Some(raw);
37 }
38
39 if let Ok(raw) = WString::from_utf16le(bytes.clone()) {
40 let utf8 = raw.to_utf8();
41 return Some(utf8.strip_prefix(BOM_LE).map(|x| x.to_string()).unwrap_or(utf8));
42 }
43
44 if let Ok(raw) = WString::from_utf16be(bytes) {
45 let utf8 = raw.to_utf8();
46 return Some(utf8.strip_prefix(BOM_BE).map(|x| x.to_string()).unwrap_or(utf8));
47 }
48
49 None
50}
51
52pub fn write_file<P: AsRef<Path>>(file: P, content: String, format: Format) -> Result<(), error::Write> {
53 match format {
54 Format::Regedit4 | Format::Wine2 => {
55 std::fs::write(file, content)?;
56 }
57 Format::Regedit5 => {
58 let content = format!("{BOM_LE}{content}");
59 let utf16 = WString::<LittleEndian>::from(&content);
60 std::fs::write(file, utf16.as_bytes())?;
61 }
62 }
63
64 Ok(())
65}