msg_tool\scripts\bgi/
bsi.rs1use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::encoding::{decode_to_string, encode_string};
6use anyhow::Result;
7use std::collections::BTreeMap;
8use std::ffi::CString;
9
10#[derive(Debug)]
11pub struct BGIBsiScriptBuilder {}
13
14impl BGIBsiScriptBuilder {
15 pub fn new() -> Self {
17 BGIBsiScriptBuilder {}
18 }
19}
20
21impl ScriptBuilder for BGIBsiScriptBuilder {
22 fn default_encoding(&self) -> Encoding {
23 Encoding::Utf8
24 }
25
26 fn build_script(
27 &self,
28 buf: Vec<u8>,
29 _filename: &str,
30 encoding: Encoding,
31 _archive_encoding: Encoding,
32 config: &ExtraConfig,
33 _archive: Option<&Box<dyn Script>>,
34 ) -> Result<Box<dyn Script>> {
35 Ok(Box::new(BGIBsiScript::new(buf, encoding, config)?))
36 }
37
38 fn extensions(&self) -> &'static [&'static str] {
39 &["_bsi"]
40 }
41
42 fn script_type(&self) -> &'static ScriptType {
43 &ScriptType::BGIBsi
44 }
45
46 fn can_create_file(&self) -> bool {
47 true
48 }
49
50 fn create_file<'a>(
51 &'a self,
52 filename: &'a str,
53 writer: Box<dyn WriteSeek + 'a>,
54 encoding: Encoding,
55 file_encoding: Encoding,
56 config: &ExtraConfig,
57 ) -> Result<()> {
58 create_file(
59 filename,
60 writer,
61 encoding,
62 file_encoding,
63 config.custom_yaml,
64 )
65 }
66}
67
68#[derive(Debug)]
69pub struct BGIBsiScript {
71 pub data: BTreeMap<String, BTreeMap<String, String>>,
73 custom_yaml: bool,
74}
75
76impl BGIBsiScript {
77 pub fn new(buf: Vec<u8>, encoding: Encoding, config: &ExtraConfig) -> Result<Self> {
83 let mut data = BTreeMap::new();
84 let mut reader = MemReader::new(buf);
85 let section_count = reader.read_u32()?;
86 for _ in 0..section_count {
87 let section_name = reader.read_cstring()?;
88 let section_name = decode_to_string(encoding, section_name.as_bytes(), true)?;
89 let mut section_data = BTreeMap::new();
90 let entry_count = reader.read_u32()?;
91 for _ in 0..entry_count {
92 let key = reader.read_cstring()?;
93 let key = decode_to_string(encoding, key.as_bytes(), true)?;
94 let value = reader.read_cstring()?;
95 let value = decode_to_string(encoding, value.as_bytes(), true)?;
96 section_data.insert(key, value);
97 }
98 data.insert(section_name, section_data);
99 }
100 if !reader.is_eof() {
101 eprintln!(
102 "Warning: BGIBsiScript data not fully read, remaining bytes: {}",
103 reader.data.len() - reader.pos
104 );
105 crate::COUNTER.inc_warning();
106 }
107 Ok(BGIBsiScript {
108 data,
109 custom_yaml: config.custom_yaml,
110 })
111 }
112}
113
114impl Script for BGIBsiScript {
115 fn default_output_script_type(&self) -> OutputScriptType {
116 OutputScriptType::Custom
117 }
118
119 fn is_output_supported(&self, output: OutputScriptType) -> bool {
120 matches!(output, OutputScriptType::Custom)
121 }
122
123 fn default_format_type(&self) -> FormatOptions {
124 FormatOptions::None
125 }
126
127 fn custom_output_extension(&self) -> &'static str {
128 if self.custom_yaml { "yaml" } else { "json" }
129 }
130
131 fn custom_export(&self, filename: &std::path::Path, encoding: Encoding) -> Result<()> {
132 let s = if self.custom_yaml {
133 serde_yaml_ng::to_string(&self.data)
134 .map_err(|e| anyhow::anyhow!("Failed to serialize to YAML: {}", e))?
135 } else {
136 serde_json::to_string(&self.data)
137 .map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))?
138 };
139 let mut writer = crate::utils::files::write_file(filename)?;
140 let s = encode_string(encoding, &s, false)?;
141 writer.write_all(&s)?;
142 writer.flush()?;
143 Ok(())
144 }
145
146 fn custom_import<'a>(
147 &'a self,
148 custom_filename: &'a str,
149 file: Box<dyn WriteSeek + 'a>,
150 encoding: Encoding,
151 output_encoding: Encoding,
152 ) -> Result<()> {
153 create_file(
154 custom_filename,
155 file,
156 encoding,
157 output_encoding,
158 self.custom_yaml,
159 )
160 }
161}
162
163fn create_file<'a>(
164 custom_filename: &'a str,
165 mut writer: Box<dyn WriteSeek + 'a>,
166 encoding: Encoding,
167 output_encoding: Encoding,
168 yaml: bool,
169) -> Result<()> {
170 let input = crate::utils::files::read_file(custom_filename)?;
171 let s = decode_to_string(output_encoding, &input, true)?;
172 let data: BTreeMap<String, BTreeMap<String, String>> = if yaml {
173 serde_yaml_ng::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse YAML: {}", e))?
174 } else {
175 serde_json::from_str(&s).map_err(|e| anyhow::anyhow!("Failed to parse JSON: {}", e))?
176 };
177 writer.write_u32(data.len() as u32)?;
178 for (section_name, section_data) in data {
179 let section_name_bytes = encode_string(encoding, §ion_name, false)?;
180 let section_name = CString::new(section_name_bytes)?;
181 writer.write_cstring(§ion_name)?;
182 writer.write_u32(section_data.len() as u32)?;
183 for (key, value) in section_data {
184 let key_bytes = encode_string(encoding, &key, false)?;
185 let key = CString::new(key_bytes)?;
186 writer.write_cstring(&key)?;
187 let value_bytes = encode_string(encoding, &value, false)?;
188 let value = CString::new(value_bytes)?;
189 writer.write_cstring(&value)?;
190 }
191 }
192 Ok(())
193}