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