1 // Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html
2
3 #include "sminfile.hh"
4 #include <assert.h>
5 #include <glib.h>
6
7 using std::string;
8 using std::vector;
9 using SpectMorph::InFile;
10 using SpectMorph::GenericIn;
11
12 /**
13 * Create InFile object for reading a file.
14 *
15 * \param filename name of the file
16 */
InFile(const string & filename)17 InFile::InFile (const string& filename) :
18 file_delete (true)
19 {
20 file = GenericIn::open (filename);
21 current_event = NONE;
22
23 read_file_type_and_version();
24 }
25
26 /**
27 * Create InFile object for reading an input stream.
28 *
29 * \param file the input stream object to read data from
30 */
InFile(GenericIn * file)31 InFile::InFile (GenericIn *file) :
32 file (file),
33 file_delete (false)
34 {
35 current_event = NONE;
36 read_file_type_and_version();
37 }
38
~InFile()39 InFile::~InFile()
40 {
41 if (file && file_delete)
42 {
43 delete file;
44 file = NULL;
45 }
46 }
47
48 void
read_file_type_and_version()49 InFile::read_file_type_and_version()
50 {
51 if (file)
52 {
53 if (file->get_byte() == 'T')
54 if (read_raw_string (m_file_type))
55 if (file->get_byte() == 'V')
56 if (read_raw_int (m_file_version))
57 return;
58 }
59 m_file_type = "unknown";
60 m_file_version = 0;
61 }
62
63 /**
64 * Get current event type.
65 *
66 * \returns current event type (or READ_ERROR or END_OF_FILE).
67 */
68 InFile::Event
event()69 InFile::event()
70 {
71 if (current_event == NONE)
72 next_event();
73
74 return current_event;
75 }
76
77 bool
read_raw_string(string & str)78 InFile::read_raw_string (string& str)
79 {
80 size_t remaining;
81 unsigned char *mem = file->mmap_mem (remaining);
82 if (mem) /* fast variant of reading strings for the mmap case */
83 {
84 for (size_t i = 0; i < remaining; i++)
85 {
86 if (mem[i] == 0)
87 {
88 if (file->skip (i + 1))
89 {
90 str.assign (reinterpret_cast <char *> (mem), i);
91 return true;
92 }
93 }
94 }
95 }
96
97 str.clear();
98
99 int c;
100 while ((c = file->get_byte()) > 0)
101 str += c;
102
103 if (c == 0)
104 return true;
105 return false;
106 }
107
108 /**
109 * Reads next event from file. Call event() to get event type, and event_*() to get event data.
110 */
111 void
next_event()112 InFile::next_event()
113 {
114 int c = file->get_byte();
115
116 if (c == 'Z') // eof
117 {
118 if (file->get_byte() == EOF) // Z needs to be followed by EOF
119 current_event = END_OF_FILE;
120 else // Z and more stuff is an error
121 current_event = READ_ERROR;
122 return;
123 }
124 else if (c == 'B')
125 {
126 current_event = READ_ERROR;
127 if (read_raw_string (current_event_str))
128 current_event = BEGIN_SECTION;
129 }
130 else if (c == 'E')
131 {
132 current_event = END_SECTION;
133 }
134 else if (c == 'f')
135 {
136 current_event = READ_ERROR;
137 if (read_raw_string (current_event_str))
138 if (read_raw_float (current_event_float))
139 current_event = FLOAT;
140 }
141 else if (c == 'i')
142 {
143 current_event = READ_ERROR;
144 if (read_raw_string (current_event_str))
145 if (read_raw_int (current_event_int))
146 current_event = INT;
147 }
148 else if (c == 'b')
149 {
150 current_event = READ_ERROR;
151 if (read_raw_string (current_event_str))
152 if (read_raw_bool (current_event_bool))
153 current_event = BOOL;
154 }
155 else if (c == 's')
156 {
157 current_event = READ_ERROR;
158 if (read_raw_string (current_event_str))
159 if (read_raw_string (current_event_data))
160 current_event = STRING;
161 }
162 else if (c == 'F')
163 {
164 current_event = READ_ERROR;
165 if (read_raw_string (current_event_str))
166 {
167 if (skip_events.find (current_event_str) != skip_events.end())
168 {
169 if (skip_raw_float_block())
170 {
171 next_event();
172 return;
173 }
174 }
175 else
176 {
177 if (read_raw_float_block (current_event_float_block))
178 current_event = FLOAT_BLOCK;
179 }
180 }
181 }
182 else if (c == '6') // 16bit block
183 {
184 current_event = READ_ERROR;
185
186 if (read_raw_string (current_event_str))
187 {
188 if (skip_events.find (current_event_str) != skip_events.end())
189 {
190 if (skip_raw_uint16_block())
191 {
192 next_event();
193 return;
194 }
195 }
196 else
197 {
198 if (read_raw_uint16_block (current_event_uint16_block))
199 current_event = UINT16_BLOCK;
200 }
201 }
202 }
203 else if (c == 'O')
204 {
205 current_event = READ_ERROR;
206 if (read_raw_string (current_event_str))
207 {
208 int blob_size;
209 if (read_raw_int (blob_size))
210 {
211 string blob_sum;
212 if (read_raw_string (blob_sum))
213 {
214 if (blob_size == -1)
215 {
216 current_event = BLOB_REF;
217 current_event_blob_sum = blob_sum;
218 }
219 else
220 {
221 int blob_pos = file->get_pos();
222 if (file->skip (blob_size)) // skip actual blob data
223 {
224 current_event = BLOB;
225 current_event_blob_size = blob_size;
226 current_event_blob_pos = blob_pos;
227 current_event_blob_sum = blob_sum;
228 }
229 }
230 }
231 }
232 }
233 }
234 else
235 {
236 current_event = READ_ERROR;
237 }
238 }
239
240 bool
read_raw_int(int & i)241 InFile::read_raw_int (int& i)
242 {
243 if (file->read (&i, 4) == 4)
244 {
245 // little endian encoding
246 i = GINT32_FROM_LE (i);
247 return true;
248 }
249 else
250 {
251 return false;
252 }
253 }
254
255 bool
read_raw_bool(bool & b)256 InFile::read_raw_bool (bool& b)
257 {
258 char bchar;
259 if (file->read (&bchar, 1) == 1)
260 {
261 if (bchar == 0)
262 {
263 b = false;
264 return true;
265 }
266 else if (bchar == 1)
267 {
268 b = true;
269 return true;
270 }
271 }
272 return false;
273 }
274
275 bool
read_raw_float(float & f)276 InFile::read_raw_float (float &f)
277 {
278 union {
279 float f;
280 int i;
281 } u;
282 bool result = read_raw_int (u.i);
283 f = u.f;
284 return result;
285 }
286
287 bool
read_raw_float_block(vector<float> & fb)288 InFile::read_raw_float_block (vector<float>& fb)
289 {
290 int size;
291 if (!read_raw_int (size))
292 return false;
293
294 fb.resize (size);
295 if (size > 0)
296 {
297 int *buffer = reinterpret_cast <int*> (&fb[0]);
298
299 if (file->read (&buffer[0], fb.size() * 4) != size * 4)
300 return false;
301
302 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
303 for (size_t x = 0; x < fb.size(); x++)
304 buffer[x] = GINT32_FROM_LE (buffer[x]);
305 #endif
306 }
307 return true;
308 }
309
310 bool
read_raw_uint16_block(vector<uint16_t> & ib)311 InFile::read_raw_uint16_block (vector<uint16_t>& ib)
312 {
313 int size;
314 if (!read_raw_int (size))
315 return false;
316
317 ib.resize (size);
318 if (size > 0)
319 {
320 if (file->read (&ib[0], ib.size() * 2) != size * 2)
321 return false;
322
323 #if G_BYTE_ORDER != G_LITTLE_ENDIAN
324 for (size_t x = 0; x < ib.size(); x++)
325 ib[x] = GUINT16_FROM_LE (ib[x]);
326 #endif
327 }
328 return true;
329 }
330
331 bool
skip_raw_float_block()332 InFile::skip_raw_float_block()
333 {
334 int size;
335 if (!read_raw_int (size))
336 return false;
337
338 return file->skip (size * 4);
339 }
340
341 bool
skip_raw_uint16_block()342 InFile::skip_raw_uint16_block()
343 {
344 int size;
345 if (!read_raw_int (size))
346 return false;
347
348 return file->skip (size * 2);
349 }
350
351 /**
352 * This function will open the blob (it will only work for BLOB events, not BLOB_REF events)
353 * and the returned GenericIn object be used to read the content of the BLOB. The caller
354 * is responsible for freeing the GenericIn object when done. NULL will be returned on
355 * error.
356 */
357 GenericIn *
open_blob()358 InFile::open_blob()
359 {
360 return file->open_subfile (current_event_blob_pos, current_event_blob_size);
361 }
362
363 /**
364 * Get name of the current event.
365 *
366 * \returns current event name
367 */
368 string
event_name()369 InFile::event_name()
370 {
371 return current_event_str;
372 }
373
374 /**
375 * Get float data of the current event (only if the event is a FLOAT).
376 *
377 * \returns current event float data.
378 */
379 float
event_float()380 InFile::event_float()
381 {
382 return current_event_float;
383 }
384
385 /**
386 * Get int data of the current event (only if the event is an INT).
387 *
388 * \returns current event int data
389 */
390 int
event_int()391 InFile::event_int()
392 {
393 return current_event_int;
394 }
395
396 /**
397 * Get bool data of the current event (only if the event is BOOL).
398 *
399 * \returns current event bool data
400 */
401 bool
event_bool()402 InFile::event_bool()
403 {
404 return current_event_bool;
405 }
406
407 /**
408 * Get string data of the current event (only if the event is STRING).
409 *
410 * \returns current event string data
411 */
412 string
event_data()413 InFile::event_data()
414 {
415 return current_event_data;
416 }
417
418 /**
419 * Get float block data of the current event (only if the event is FLOAT_BLOCK).
420 *
421 * \returns current event float block data (by reference)
422 */
423 const vector<float>&
event_float_block()424 InFile::event_float_block()
425 {
426 return current_event_float_block;
427 }
428
429 /**
430 * Get uint16 block data of the current event (only if the event is UINT16_BLOCK).
431 *
432 * \returns current event uint16 block data (by reference)
433 */
434 const vector<uint16_t>&
event_uint16_block()435 InFile::event_uint16_block()
436 {
437 return current_event_uint16_block;
438 }
439
440 /**
441 * Get blob's checksum. This works for both: BLOB objects and BLOB_REF
442 * objects. During writing files, the first occurence of a BLOB is stored
443 * completely, whereas after that, only the blob sum is stored as BLOB_REF
444 * event. During loading, code for handling both needs to be supplied.
445 *
446 * \returns the blob's checksum
447 */
448 string
event_blob_sum()449 InFile::event_blob_sum()
450 {
451 return current_event_blob_sum;
452 }
453
454 /**
455 * Add event names to skip (currently only implemented for FLOAT_BLOCK events); this
456 * speeds up reading files, while ignoring certain events.
457 *
458 * \param skip_event name of the event to skip
459 */
460 void
add_skip_event(const string & skip_event)461 InFile::add_skip_event (const string& skip_event)
462 {
463 skip_events.insert (skip_event);
464 }
465
466 /**
467 * Get file type (usually a class name, like "SpectMorph::WavSet").
468 *
469 * \returns file type
470 */
471 string
file_type()472 InFile::file_type()
473 {
474 return m_file_type;
475 }
476
477 /**
478 * Get file version, an integer.
479 *
480 * \returns file version
481 */
482 int
file_version()483 InFile::file_version()
484 {
485 return m_file_version;
486 }
487