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