Fawkes API  Fawkes Development Version
fvfile.cpp
00001 
00002 /***************************************************************************
00003  *  fvfile.cpp - FireVision file
00004  *
00005  *  Created: Fri Mar 28 11:45:47 2008
00006  *  Copyright  2008  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <fvutils/fileformat/fvfile.h>
00025 
00026 #include <core/exceptions/system.h>
00027 #include <utils/misc/strndup.h>
00028 
00029 #include <cstring>
00030 #include <cstdio>
00031 #include <cerrno>
00032 #include <netinet/in.h>
00033 #include <sys/time.h>
00034 
00035 using namespace fawkes;
00036 
00037 namespace firevision {
00038 #if 0 /* just to make Emacs auto-indent happy */
00039 }
00040 #endif
00041 
00042 /** @class FireVisionDataFile <fvutils/fileformat/fvff.h>
00043  * FireVision File Format for data files.
00044  * The FireVision File Format (FVFF) defines a generic file layout that is used
00045  * to store large chunks of data on the the disk drive in a byte efficient way.
00046  *
00047  * It is meant to serve as a skeleton which is used by subclasses to implement
00048  * support for a concrete file format like colormaps or rectification information.
00049  * It allows for arbitrary meta data to be added that is relevant to the format and
00050  * it provides all the generic meta data that is needed to make the file format
00051  * work and that is common to all formats.
00052  *
00053  * Each format has a two byte magic token. In general it is of the form FFNN, where
00054  * FF stays literally (FireVision File) and NN is replaced with a number of the format.
00055  * Currently assigned format numbers include:
00056  * - FF01: colormaps
00057  * - FF02: generic lookup tables
00058  * - FF03: rectification information
00059  * - FF04: histograms
00060  *
00061  * We assume large chunks of data that is saved most efficiently in a proprietary
00062  * binary format that can be read and written quickly and mimics the layout of the
00063  * file in the memory.
00064  *
00065  * The general layout is:
00066  * @code
00067  *  1. General header (file type, version, endianess, number of blocks, etc.)
00068  *  2. Content type specific header (optional)
00069  *  3. Data blocks
00070  * @endcode
00071  * Each of the data blocks itself is of the following form:
00072  * @code
00073  *  1. General block header (type, size)
00074  *  2. Content type specific block header (optional)
00075  *  3. Data chunk (raw byte stream, content-specific)
00076  * @endcode
00077  *
00078  * @author Tim Niemueller
00079  */
00080 
00081 /** @var void * FireVisionDataFile::_spec_header
00082  * Content specific header.
00083  * Create this buffer and set the size in _spec_header_size to get it written to
00084  * the file.
00085  */
00086 /** @var size_t FireVisionDataFile::_spec_header_size
00087  * Size in bytes of _spec_header.
00088  */
00089 
00090 
00091 /** Constructor.
00092  * @param magic_token magic token for the concrete file type
00093  * @param version file format version
00094  */
00095 FireVisionDataFile::FireVisionDataFile(unsigned short int magic_token,
00096                                        unsigned short int version)
00097 {
00098   __header       = (fvff_header_t *)calloc(1, sizeof(fvff_header_t));
00099 
00100   __magic_token = magic_token;
00101   __version     = version;
00102   __comment     = strdup("");
00103 
00104   _spec_header = NULL;
00105   _spec_header_size = 0;
00106 
00107   __owns_blocks = true;
00108 
00109   clear();
00110 }
00111 
00112 
00113 /** Destructor. */
00114 FireVisionDataFile::~FireVisionDataFile()
00115 {
00116   clear();
00117 
00118   free(__header);
00119   free(__comment);
00120   if ( _spec_header ) {
00121     free(_spec_header);
00122   }
00123 }
00124 
00125 
00126 /** Clear internal storage.
00127  * All internal data is deleted.
00128  */
00129 void
00130 FireVisionDataFile::clear()
00131 {
00132   if (__owns_blocks) {
00133     for (__bi = __blocks.begin(); __bi != __blocks.end(); ++__bi) {
00134       delete *__bi;
00135     }
00136   }
00137 
00138   __blocks.clear();
00139   memset(__header, 0, sizeof(fvff_header_t));
00140 
00141   __header->magic_token = htons(__magic_token);
00142   __header->version     = __version;
00143   __header->num_blocks = 0;
00144 #if __BYTE_ORDER == __BIG_ENDIAN
00145   __header->endianess = 1;
00146 #else
00147   __header->endianess = 0;
00148 #endif
00149   free(__comment);
00150   __comment = strdup("");
00151 }
00152 
00153 
00154 /** Get the magic token of the file.
00155  * @return Magic token
00156  */
00157 unsigned int
00158 FireVisionDataFile::magic_token()
00159 {
00160   return __header->magic_token;
00161 }
00162 
00163 
00164 /** Get the version of the file.
00165  * @return version of the file (or the current supported version if no file was loaded)
00166  */
00167 unsigned int
00168 FireVisionDataFile::version()
00169 {
00170   return __header->version;
00171 }
00172 
00173 
00174 /** Check if data is encoded as big endian.
00175  * @return true if data is encoded as big endian, false otherwise
00176  */
00177 bool
00178 FireVisionDataFile::is_big_endian()
00179 {
00180   return (__header->endianess == 1);
00181 }
00182 
00183 
00184 /** Check if data is encoded as little endian.
00185  * @return true if data is encoded as little endian, false otherwise
00186  */
00187 bool
00188 FireVisionDataFile::is_little_endian()
00189 {
00190   return (__header->endianess == 0);
00191 }
00192 
00193 
00194 /** Get comment.
00195  * @return comment of the file
00196  */
00197 const char *
00198 FireVisionDataFile::get_comment() const
00199 {
00200   return __comment;
00201 }
00202 
00203 
00204 /** Set comment.
00205  * @param comment new comment to set
00206  */
00207 void
00208 FireVisionDataFile::set_comment(const char *comment)
00209 {
00210   free(__comment);
00211   __comment = strndup(comment, FVFF_COMMENT_SIZE);
00212   strncpy(__header->comment, comment, FVFF_COMMENT_SIZE);
00213 }
00214 
00215 
00216 /** Lets the file take over the ownership and give up the ownership of the blocks,
00217  * respectively. By default, the file is the owner of the blocks. If a file owns
00218  * the blocks they will be deleted in the files destructor.
00219  * @param owns_blocks if true file owns the blocks
00220  */
00221 void
00222 FireVisionDataFile::set_owns_blocks(bool owns_blocks)
00223 {
00224   __owns_blocks = owns_blocks;
00225 }
00226 
00227 
00228 /** Get the number of available info blocks.
00229  * @return number of available info blocks
00230  */
00231 size_t
00232 FireVisionDataFile::num_blocks()
00233 {
00234   return __blocks.size();
00235 }
00236 
00237 
00238 /** Add a block.
00239  * @param block block to add
00240  */
00241 void
00242 FireVisionDataFile::add_block(FireVisionDataFileBlock *block)
00243 {
00244   __blocks.push_back(block);
00245 }
00246 
00247 
00248 /** Get blocks.
00249  * @return block list
00250  */
00251 FireVisionDataFile::BlockList &
00252 FireVisionDataFile::blocks()
00253 {
00254   return __blocks;
00255 }
00256 
00257 
00258 /** Write file.
00259  * @param file_name file to write to
00260  */
00261 void
00262 FireVisionDataFile::write(const char *file_name)
00263 {
00264   FILE *f = fopen(file_name, "w");
00265   if ( f == NULL ) {
00266     throw CouldNotOpenFileException(file_name, errno, "Could not open rectlut file "
00267                                                       "for writing");
00268   }
00269 
00270   __header->num_blocks = (unsigned int)__blocks.size();
00271   timeval t;
00272   gettimeofday(&t, NULL);
00273   __header->created_sec  = t.tv_sec;
00274   __header->created_usec = t.tv_usec;
00275   __header->spec_head_size = _spec_header_size;
00276 
00277   //printf("Writing %zu bytes for header\n", sizeof(fvff_header_t));
00278   if ( fwrite(__header, sizeof(fvff_header_t), 1, f) != 1 ) {
00279     fclose(f);
00280     throw FileWriteException(file_name, errno, "Writing fvff header failed");
00281   }
00282 
00283   if ( _spec_header_size > 0 ) {
00284     //printf("Writing %zu bytes for spec header\n", _spec_header_size);
00285     if ( fwrite(_spec_header, _spec_header_size, 1, f) != 1 ) {
00286       fclose(f);
00287       throw FileWriteException(file_name, errno, "Writing content specific header failed");
00288     }
00289   }
00290 
00291   for (__bi = __blocks.begin(); __bi != __blocks.end(); ++__bi) {
00292     // write this info block
00293     //printf("Writing %zu bytes for block\n", (*__bi)->block_size());
00294     if ( fwrite((*__bi)->block_memptr(), (*__bi)->block_size(), 1, f) != 1 ) {
00295       fclose(f);
00296       throw FileWriteException(file_name, errno, "Failed to write info block");
00297     }
00298   }
00299 
00300   fclose(f);
00301 }
00302 
00303 
00304 /** Read file.
00305  * @param file_name file to read from
00306  */
00307 void
00308 FireVisionDataFile::read(const char *file_name)
00309 {
00310   FILE *f = fopen(file_name, "r");
00311   if ( f == NULL ) {
00312     throw CouldNotOpenFileException(file_name, errno, "Could not open rectlut file "
00313                                                       "for reading");
00314   }
00315 
00316   clear();
00317 
00318   //printf("Reading %zu bytes for header\n", sizeof(fvff_header_t));
00319   if ( fread(__header, sizeof(fvff_header_t), 1, f) != 1) {
00320     fclose(f);
00321     throw FileReadException(file_name, errno, "Reading header failed");
00322   }
00323 
00324   if ( __header->magic_token != htons(__magic_token) ) {
00325     fclose(f);
00326     throw Exception("Unknown magic in fvff file (read: 0x%04x req: 0x%04x)",
00327                     __header->magic_token, __magic_token);
00328   }
00329 
00330   if ( __header->version != __version ) {
00331     fclose(f);
00332     throw Exception("Unsupported version of fvff file (read: %u req: %u)",
00333                     __header->version, __version);
00334   }
00335 
00336   if ( __header->endianess ==
00337 #if __BYTE_ORDER == __BIG_ENDIAN
00338        0
00339 #else
00340        1
00341 #endif
00342        ) {
00343     fclose(f);
00344     throw Exception("FVFile header cannot be translated for endianess by now");
00345   }
00346 
00347   free(__comment);
00348   __comment = strndup(__header->comment, FVFF_COMMENT_SIZE);
00349 
00350   if ( _spec_header ) {
00351     free(_spec_header);
00352   }
00353   _spec_header = calloc(1, __header->spec_head_size);
00354   if ( ! _spec_header ) {
00355     throw OutOfMemoryException("Cannot allocate memory for content specific header");
00356   }
00357 
00358   if ( __header->spec_head_size > 0 ) {
00359     //printf("Reading %u bytes for spec header\n", __header->spec_head_size);
00360     if ( fread(_spec_header, __header->spec_head_size, 1, f) != 1) {
00361       fclose(f);
00362       throw FileReadException(file_name, errno, "Reading content specific header failed");
00363     }
00364   }
00365 
00366   //printf("Reading %u blocks\n", __header->num_blocks);
00367   for (uint8_t b = 0; b < __header->num_blocks && !feof(f); ++b) {
00368     fvff_block_header_t bh;
00369     //printf("Reading %zu bytes for block header\n", sizeof(bh));
00370     if ( fread(&bh, sizeof(bh), 1, f) != 1 ) {
00371       fclose(f);
00372       throw FileReadException(file_name, errno,
00373                               "Could not read block info header while there should be one");
00374     }
00375     void *spec_header = NULL;
00376 
00377     if ( bh.spec_head_size > 0 ) {
00378       // Read specific header
00379       spec_header = malloc(bh.spec_head_size);
00380       if ( ! spec_header ) {
00381         throw OutOfMemoryException("Could not allocate %u bytes for content specific header",
00382                                    bh.spec_head_size);
00383       }
00384 
00385       //printf("Reading %u bytes for block spec header\n", bh.spec_head_size);
00386       if ( fread(spec_header, bh.spec_head_size, 1, f) != 1 ) {
00387         fclose(f);
00388         free(spec_header);
00389         throw FileReadException(file_name, errno,
00390                                 "Could not read content specific block header");
00391       }
00392     }
00393 
00394     FireVisionDataFileBlock *block = new FireVisionDataFileBlock(bh.type, bh.size,
00395                                                                  spec_header, bh.spec_head_size);
00396 
00397     free(spec_header);
00398 
00399     //printf("Reading %u bytes for block data\n", bh.size);
00400     if ( bh.size && fread(block->data_ptr(), bh.size, 1, f) != 1 ) {
00401       fclose(f);
00402       delete block;
00403       throw FileReadException(file_name, errno,
00404                               "Could not read block data");
00405     }
00406 
00407     __blocks.push_back(block);
00408   }
00409 
00410   fclose(f);
00411 }
00412 
00413 
00414 /** Get magic token from file.
00415  * @param filename name of file to read the magic token from
00416  * @return magic token
00417  */
00418 unsigned short int
00419 FireVisionDataFile::read_magic_token(const char *filename)
00420 {
00421   uint16_t magic_token = 0;
00422 
00423   FILE *f;
00424   f = fopen(filename, "r");
00425   if (f != NULL) {
00426     if ( fread((char *)&magic_token, sizeof(magic_token), 1, f) != 1 ) {
00427       fclose(f);
00428       throw FileReadException(filename, errno, "Could not read magic token from file");
00429     }
00430     fclose(f);
00431   } else {
00432     throw FileReadException(filename, errno,
00433                             "Could not read magic token from file");
00434   }
00435 
00436   return magic_token;
00437 }
00438 
00439 
00440 /** Check if file has a certain magic token.
00441  * @param filename name of file to read the magic token from
00442  * @param magic_token magic token to look for
00443  * @return true if magic token was  found, false otherwise
00444  */
00445 bool
00446 FireVisionDataFile::has_magic_token(const char *filename, unsigned short int magic_token)
00447 {
00448   uint16_t file_magic_token = read_magic_token(filename);
00449   return (htons(magic_token) == file_magic_token);
00450 }
00451 
00452 } // end namespace firevision