1use crate::{NbsError, NbsFormat};
2use byteorder::LittleEndian;
3use std::time::Duration;
4
5#[derive(Debug)]
7pub struct Header {
8 pub(crate) old_song_length: i16,
11 pub(crate) version_number: Option<i8>,
14 pub vannila_instrument_count: Option<i8>,
18 pub(crate) song_length: Option<i16>,
22 pub layer_count: i16,
24 pub song_name: String,
26 pub song_author: String,
28 pub original_song_author: String,
30 pub song_description: String,
32 pub song_tempo: i16,
34 pub auto_saving: bool,
37 pub auto_saving_duration: i8,
40 pub time_signature: i8,
43 pub minutes_spent: i32,
45 pub left_clicks: i32,
47 pub right_clicks: i32,
49 pub noteblocks_added: i32,
51 pub noteblocks_removed: i32,
53 pub imported_file_name: String,
55 pub is_loop: Option<bool>,
58 pub max_loop_count: Option<i8>,
61 pub loop_start_tick: Option<i16>,
64 pub format: NbsFormat,
66}
67
68impl Header {
69 pub fn new(format: NbsFormat) -> Self {
70 Header {
71 old_song_length: 0,
72 version_number: Some(format.version()),
73 vannila_instrument_count: Some(16),
74 song_length: Some(0),
75 layer_count: 0,
76 song_name: String::new(),
77 song_author: String::new(),
78 original_song_author: String::new(),
79 song_description: String::new(),
80 song_tempo: 1000,
81 auto_saving: false,
82 auto_saving_duration: 0,
83 time_signature: 4,
84 minutes_spent: 0,
85 left_clicks: 0,
86 right_clicks: 0,
87 noteblocks_added: 0,
88 noteblocks_removed: 0,
89 imported_file_name: String::new(),
90 is_loop: Some(false),
91 max_loop_count: Some(0),
92 loop_start_tick: Some(0),
93 format: format,
94 }
95 }
96
97 pub fn decode<R>(reader: &mut R) -> Result<Self, NbsError>
98 where
99 R: crate::ReadStringExt,
100 {
101 let old_song_length = reader.read_i16::<LittleEndian>()?;
102 let version = if old_song_length != 0 {
103 NbsFormat::NoteBlockStudio
104 } else {
105 NbsFormat::OpenNoteBlockStudio(reader.read_i8()?)
106 };
107 let version_number = match version {
108 NbsFormat::NoteBlockStudio => None,
109 NbsFormat::OpenNoteBlockStudio(v) => Some(v),
110 };
111 let vannila_instrument_count = if version.is_new() {
112 Some(reader.read_i8()?)
113 } else {
114 None
115 };
116 let song_length = if version.is_new() {
117 Some(reader.read_i16::<LittleEndian>()?)
118 } else {
119 None
120 };
121 let layer_count = reader.read_i16::<LittleEndian>()?;
122 let song_name = reader.read_string()?;
123 let song_author = reader.read_string()?;
124 let original_song_author = reader.read_string()?;
125 let song_description = reader.read_string()?;
126 let song_tempo = reader.read_i16::<LittleEndian>()?;
127 let auto_saving = if reader.read_i8()? == 1 { true } else { false };
128 let auto_saving_duration = reader.read_i8()?;
129 let time_signature = reader.read_i8()?;
130 let minutes_spent = reader.read_i32::<LittleEndian>()?;
131 let left_clicks = reader.read_i32::<LittleEndian>()?;
132 let right_clicks = reader.read_i32::<LittleEndian>()?;
133 let noteblocks_added = reader.read_i32::<LittleEndian>()?;
134 let noteblocks_removed = reader.read_i32::<LittleEndian>()?;
135 let imported_file_name = reader.read_string()?;
136 let is_loop = if version.is_new() {
137 Some(if reader.read_i8()? == 1 { true } else { false })
138 } else {
139 None
140 };
141 let max_loop_count = if version.is_new() {
142 Some(reader.read_i8()?)
143 } else {
144 None
145 };
146 let loop_start_tick = if version.is_new() {
147 Some(reader.read_i16::<LittleEndian>()?)
148 } else {
149 None
150 };
151 Ok(Header {
152 old_song_length,
153 version_number,
154 vannila_instrument_count,
155 song_length,
156 layer_count,
157 song_name,
158 song_author,
159 original_song_author,
160 song_description,
161 song_tempo,
162 auto_saving,
163 auto_saving_duration,
164 time_signature,
165 minutes_spent,
166 left_clicks,
167 right_clicks,
168 noteblocks_added,
169 noteblocks_removed,
170 imported_file_name,
171 is_loop,
172 max_loop_count,
173 loop_start_tick,
174 format: version,
175 })
176 }
177
178 pub fn encode<W>(&self, format: NbsFormat, writer: &mut W) -> Result<(), NbsError>
179 where
180 W: crate::WriteStringExt,
181 {
182 writer.write_i16::<LittleEndian>(self.old_song_length)?;
183 if format.version() > 0 {
184 writer.write_i8(self.version_number.ok_or(NbsError::InvalidFormat)?)?;
185 writer.write_i8(
186 self.vannila_instrument_count
187 .ok_or(NbsError::InvalidFormat)?,
188 )?;
189 }
190 if format.version() >= 3 {
191 writer.write_i16::<LittleEndian>(self.song_length.ok_or(NbsError::InvalidFormat)?)?;
192 }
193 writer.write_i16::<LittleEndian>(self.layer_count)?;
194 writer.write_string(&self.song_name)?;
195 writer.write_string(&self.song_author)?;
196 writer.write_string(&self.original_song_author)?;
197 writer.write_string(&self.song_description)?;
198 writer.write_i16::<LittleEndian>(self.song_tempo)?;
199 writer.write_i8(if self.auto_saving { 1 } else { 0 })?;
200 writer.write_i8(self.auto_saving_duration)?;
201 writer.write_i8(self.time_signature)?;
202 writer.write_i32::<LittleEndian>(self.minutes_spent)?;
203 writer.write_i32::<LittleEndian>(self.left_clicks)?;
204 writer.write_i32::<LittleEndian>(self.right_clicks)?;
205 writer.write_i32::<LittleEndian>(self.noteblocks_added)?;
206 writer.write_i32::<LittleEndian>(self.noteblocks_removed)?;
207 writer.write_string(&self.imported_file_name)?;
208 if format.version() > 0 {
209 writer.write_i8(if self.is_loop.ok_or(NbsError::InvalidFormat)? {
210 1
211 } else {
212 0
213 })?;
214 writer.write_i8(self.max_loop_count.ok_or(NbsError::InvalidFormat)?)?;
215 writer
216 .write_i16::<LittleEndian>(self.loop_start_tick.ok_or(NbsError::InvalidFormat)?)?;
217 }
218
219 Ok(())
220 }
221
222 pub fn vannila_instrument_count(&self) -> Result<i8, NbsError> {
223 Ok(match self.format {
224 NbsFormat::NoteBlockStudio => 10,
225 NbsFormat::OpenNoteBlockStudio(_) => self
226 .vannila_instrument_count
227 .ok_or(NbsError::InvalidFormat)?,
228 })
229 }
230
231 pub fn song_ticks(&self) -> Result<Option<i16>, NbsError> {
234 Ok(match self.format {
235 NbsFormat::NoteBlockStudio => Some(self.old_song_length),
236 NbsFormat::OpenNoteBlockStudio(v) => {
237 if v >= 3 {
238 Some(self.song_length.ok_or(NbsError::InvalidFormat)?)
239 } else {
240 None
241 }
242 }
243 })
244 }
245
246 pub fn song_length(&self) -> Result<Option<Duration>, NbsError> {
249 let song_ticks = self.song_ticks()?;
250 if song_ticks.is_none() {
251 return Ok(None);
252 }
253 Ok(Some(Duration::from_secs_f32(
254 song_ticks.unwrap() as f32 / (self.song_tempo as f32 / 100.0),
255 )))
256 }
257}