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 #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