Fawkes API  Fawkes Development Version
fvfile.cpp
1 
2 /***************************************************************************
3  * fvfile.cpp - FireVision file
4  *
5  * Created: Fri Mar 28 11:45:47 2008
6  * Copyright 2008 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <fvutils/fileformat/fvfile.h>
25 
26 #include <core/exceptions/system.h>
27 #include <utils/misc/strndup.h>
28 
29 #include <cstring>
30 #include <cstdio>
31 #include <cerrno>
32 #include <netinet/in.h>
33 #include <sys/time.h>
34 
35 using namespace fawkes;
36 
37 namespace firevision {
38 #if 0 /* just to make Emacs auto-indent happy */
39 }
40 #endif
41 
42 /** @class FireVisionDataFile <fvutils/fileformat/fvff.h>
43  * FireVision File Format for data files.
44  * The FireVision File Format (FVFF) defines a generic file layout that is used
45  * to store large chunks of data on the the disk drive in a byte efficient way.
46  *
47  * It is meant to serve as a skeleton which is used by subclasses to implement
48  * support for a concrete file format like colormaps or rectification information.
49  * It allows for arbitrary meta data to be added that is relevant to the format and
50  * it provides all the generic meta data that is needed to make the file format
51  * work and that is common to all formats.
52  *
53  * Each format has a two byte magic token. In general it is of the form FFNN, where
54  * FF stays literally (FireVision File) and NN is replaced with a number of the format.
55  * Currently assigned format numbers include:
56  * - FF01: colormaps
57  * - FF02: generic lookup tables
58  * - FF03: rectification information
59  * - FF04: histograms
60  *
61  * We assume large chunks of data that is saved most efficiently in a proprietary
62  * binary format that can be read and written quickly and mimics the layout of the
63  * file in the memory.
64  *
65  * The general layout is:
66  * @code
67  * 1. General header (file type, version, endianess, number of blocks, etc.)
68  * 2. Content type specific header (optional)
69  * 3. Data blocks
70  * @endcode
71  * Each of the data blocks itself is of the following form:
72  * @code
73  * 1. General block header (type, size)
74  * 2. Content type specific block header (optional)
75  * 3. Data chunk (raw byte stream, content-specific)
76  * @endcode
77  *
78  * @author Tim Niemueller
79  */
80 
81 /** @var void * FireVisionDataFile::_spec_header
82  * Content specific header.
83  * Create this buffer and set the size in _spec_header_size to get it written to
84  * the file.
85  */
86 /** @var size_t FireVisionDataFile::_spec_header_size
87  * Size in bytes of _spec_header.
88  */
89 
90 
91 /** Constructor.
92  * @param magic_token magic token for the concrete file type
93  * @param version file format version
94  */
95 FireVisionDataFile::FireVisionDataFile(unsigned short int magic_token,
96  unsigned short int version)
97 {
98  __header = (fvff_header_t *)calloc(1, sizeof(fvff_header_t));
99 
100  __magic_token = magic_token;
101  __version = version;
102  __comment = strdup("");
103 
104  _spec_header = NULL;
105  _spec_header_size = 0;
106 
107  __owns_blocks = true;
108 
109  clear();
110 }
111 
112 
113 /** Destructor. */
114 FireVisionDataFile::~FireVisionDataFile()
115 {
116  clear();
117 
118  free(__header);
119  free(__comment);
120  if ( _spec_header ) {
121  free(_spec_header);
122  }
123 }
124 
125 
126 /** Clear internal storage.
127  * All internal data is deleted.
128  */
129 void
130 FireVisionDataFile::clear()
131 {
132  if (__owns_blocks) {
133  for (__bi = __blocks.begin(); __bi != __blocks.end(); ++__bi) {
134  delete *__bi;
135  }
136  }
137 
138  __blocks.clear();
139  memset(__header, 0, sizeof(fvff_header_t));
140 
141  __header->magic_token = htons(__magic_token);
142  __header->version = __version;
143  __header->num_blocks = 0;
144 #if __BYTE_ORDER == __BIG_ENDIAN
145  __header->endianess = 1;
146 #else
147  __header->endianess = 0;
148 #endif
149  free(__comment);
150  __comment = strdup("");
151 }
152 
153 
154 /** Get the magic token of the file.
155  * @return Magic token
156  */
157 unsigned int
158 FireVisionDataFile::magic_token()
159 {
160  return __header->magic_token;
161 }
162 
163 
164 /** Get the version of the file.
165  * @return version of the file (or the current supported version if no file was loaded)
166  */
167 unsigned int
168 FireVisionDataFile::version()
169 {
170  return __header->version;
171 }
172 
173 
174 /** Check if data is encoded as big endian.
175  * @return true if data is encoded as big endian, false otherwise
176  */
177 bool
178 FireVisionDataFile::is_big_endian()
179 {
180  return (__header->endianess == 1);
181 }
182 
183 
184 /** Check if data is encoded as little endian.
185  * @return true if data is encoded as little endian, false otherwise
186  */
187 bool
188 FireVisionDataFile::is_little_endian()
189 {
190  return (__header->endianess == 0);
191 }
192 
193 
194 /** Get comment.
195  * @return comment of the file
196  */
197 const char *
198 FireVisionDataFile::get_comment() const
199 {
200  return __comment;
201 }
202 
203 
204 /** Set comment.
205  * @param comment new comment to set
206  */
207 void
208 FireVisionDataFile::set_comment(const char *comment)
209 {
210  free(__comment);
211  __comment = strndup(comment, FVFF_COMMENT_SIZE);
212  strncpy(__header->comment, comment, FVFF_COMMENT_SIZE);
213 }
214 
215 
216 /** Lets the file take over the ownership and give up the ownership of the blocks,
217  * respectively. By default, the file is the owner of the blocks. If a file owns
218  * the blocks they will be deleted in the files destructor.
219  * @param owns_blocks if true file owns the blocks
220  */
221 void
222 FireVisionDataFile::set_owns_blocks(bool owns_blocks)
223 {
224  __owns_blocks = owns_blocks;
225 }
226 
227 
228 /** Get the number of available info blocks.
229  * @return number of available info blocks
230  */
231 size_t
232 FireVisionDataFile::num_blocks()
233 {
234  return __blocks.size();
235 }
236 
237 
238 /** Add a block.
239  * @param block block to add
240  */
241 void
242 FireVisionDataFile::add_block(FireVisionDataFileBlock *block)
243 {
244  __blocks.push_back(block);
245 }
246 
247 
248 /** Get blocks.
249  * @return block list
250  */
252 FireVisionDataFile::blocks()
253 {
254  return __blocks;
255 }
256 
257 
258 /** Write file.
259  * @param file_name file to write to
260  */
261 void
262 FireVisionDataFile::write(const char *file_name)
263 {
264  FILE *f = fopen(file_name, "w");
265  if ( f == NULL ) {
266  throw CouldNotOpenFileException(file_name, errno, "Could not open rectlut file "
267  "for writing");
268  }
269 
270  __header->num_blocks = (unsigned int)__blocks.size();
271  timeval t;
272  gettimeofday(&t, NULL);
273  __header->created_sec = t.tv_sec;
274  __header->created_usec = t.tv_usec;
275  __header->spec_head_size = _spec_header_size;
276 
277  //printf("Writing %zu bytes for header\n", sizeof(fvff_header_t));
278  if ( fwrite(__header, sizeof(fvff_header_t), 1, f) != 1 ) {
279  fclose(f);
280  throw FileWriteException(file_name, errno, "Writing fvff header failed");
281  }
282 
283  if ( _spec_header_size > 0 ) {
284  //printf("Writing %zu bytes for spec header\n", _spec_header_size);
285  if ( fwrite(_spec_header, _spec_header_size, 1, f) != 1 ) {
286  fclose(f);
287  throw FileWriteException(file_name, errno, "Writing content specific header failed");
288  }
289  }
290 
291  for (__bi = __blocks.begin(); __bi != __blocks.end(); ++__bi) {
292  // write this info block
293  //printf("Writing %zu bytes for block\n", (*__bi)->block_size());
294  if ( fwrite((*__bi)->block_memptr(), (*__bi)->block_size(), 1, f) != 1 ) {
295  fclose(f);
296  throw FileWriteException(file_name, errno, "Failed to write info block");
297  }
298  }
299 
300  fclose(f);
301 }
302 
303 
304 /** Read file.
305  * @param file_name file to read from
306  */
307 void
308 FireVisionDataFile::read(const char *file_name)
309 {
310  FILE *f = fopen(file_name, "r");
311  if ( f == NULL ) {
312  throw CouldNotOpenFileException(file_name, errno, "Could not open rectlut file "
313  "for reading");
314  }
315 
316  clear();
317 
318  //printf("Reading %zu bytes for header\n", sizeof(fvff_header_t));
319  if ( fread(__header, sizeof(fvff_header_t), 1, f) != 1) {
320  fclose(f);
321  throw FileReadException(file_name, errno, "Reading header failed");
322  }
323 
324  if ( __header->magic_token != htons(__magic_token) ) {
325  fclose(f);
326  throw Exception("Unknown magic in fvff file (read: 0x%04x req: 0x%04x)",
327  __header->magic_token, __magic_token);
328  }
329 
330  if ( __header->version != __version ) {
331  fclose(f);
332  throw Exception("Unsupported version of fvff file (read: %u req: %u)",
333  __header->version, __version);
334  }
335 
336  if ( __header->endianess ==
337 #if __BYTE_ORDER == __BIG_ENDIAN
338  0
339 #else
340  1
341 #endif
342  ) {
343  fclose(f);
344  throw Exception("FVFile header cannot be translated for endianess by now");
345  }
346 
347  free(__comment);
348  __comment = strndup(__header->comment, FVFF_COMMENT_SIZE);
349 
350  if ( _spec_header ) {
351  free(_spec_header);
352  }
353  _spec_header = calloc(1, __header->spec_head_size);
354  if ( ! _spec_header ) {
355  throw OutOfMemoryException("Cannot allocate memory for content specific header");
356  }
357 
358  if ( __header->spec_head_size > 0 ) {
359  //printf("Reading %u bytes for spec header\n", __header->spec_head_size);
360  if ( fread(_spec_header, __header->spec_head_size, 1, f) != 1) {
361  fclose(f);
362  throw FileReadException(file_name, errno, "Reading content specific header failed");
363  }
364  }
365 
366  //printf("Reading %u blocks\n", __header->num_blocks);
367  for (unsigned int b = 0; b < __header->num_blocks && !feof(f); ++b) {
369  //printf("Reading %zu bytes for block header\n", sizeof(bh));
370  if ( fread(&bh, sizeof(bh), 1, f) != 1 ) {
371  fclose(f);
372  throw FileReadException(file_name, errno,
373  "Could not read block info header while there should be one");
374  }
375  void *spec_header = NULL;
376 
377  if ( bh.spec_head_size > 0 ) {
378  // Read specific header
379  spec_header = malloc(bh.spec_head_size);
380  if ( ! spec_header ) {
381  throw OutOfMemoryException("Could not allocate %u bytes for content specific header",
382  bh.spec_head_size);
383  }
384 
385  //printf("Reading %u bytes for block spec header\n", bh.spec_head_size);
386  if ( fread(spec_header, bh.spec_head_size, 1, f) != 1 ) {
387  fclose(f);
388  free(spec_header);
389  throw FileReadException(file_name, errno,
390  "Could not read content specific block header");
391  }
392  }
393 
395  spec_header, bh.spec_head_size);
396 
397  free(spec_header);
398 
399  //printf("Reading %u bytes for block data\n", bh.size);
400  if ( bh.size && fread(block->data_ptr(), bh.size, 1, f) != 1 ) {
401  fclose(f);
402  delete block;
403  throw FileReadException(file_name, errno,
404  "Could not read block data");
405  }
406 
407  __blocks.push_back(block);
408  }
409 
410  fclose(f);
411 }
412 
413 
414 /** Get magic token from file.
415  * @param filename name of file to read the magic token from
416  * @return magic token
417  */
418 unsigned short int
419 FireVisionDataFile::read_magic_token(const char *filename)
420 {
421  uint16_t magic_token = 0;
422 
423  FILE *f;
424  f = fopen(filename, "r");
425  if (f != NULL) {
426  if ( fread((char *)&magic_token, sizeof(magic_token), 1, f) != 1 ) {
427  fclose(f);
428  throw FileReadException(filename, errno, "Could not read magic token from file");
429  }
430  fclose(f);
431  } else {
432  throw FileReadException(filename, errno,
433  "Could not read magic token from file");
434  }
435 
436  return magic_token;
437 }
438 
439 
440 /** Check if file has a certain magic token.
441  * @param filename name of file to read the magic token from
442  * @param magic_token magic token to look for
443  * @return true if magic token was found, false otherwise
444  */
445 bool
446 FireVisionDataFile::has_magic_token(const char *filename, unsigned short int magic_token)
447 {
448  uint16_t file_magic_token = read_magic_token(filename);
449  return (htons(magic_token) == file_magic_token);
450 }
451 
452 } // end namespace firevision
File could not be opened.
Definition: system.h:53
File could not be read.
Definition: system.h:61
uint32_t size
size in bytes of this block, does not include any headers
Definition: fvff.h:78
Fawkes library namespace.
FireVision File Format data block.
Definition: fvfile_block.h:35
std::list< FireVisionDataFileBlock * > BlockList
List of FireVision data file blocks.
Definition: fvfile.h:64
void * data_ptr() const
Get data pointer.
Base class for exceptions in Fawkes.
Definition: exception.h:36
Could not write to file.
Definition: system.h:68
uint32_t spec_head_size
the size of the following content specific block header
Definition: fvff.h:79
Header for a FireVision file format file.
Definition: fvff.h:57
uint32_t type
The type of the block, content-specific.
Definition: fvff.h:77
System ran out of memory and desired operation could not be fulfilled.
Definition: system.h:32