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