File indexing completed on 2024-03-24 15:14:49
0001 /* 0002 SPDX-FileCopyrightText: 2008 Akarsh Simha <akarshsimha@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #pragma once 0008 0009 #include <QString> 0010 #include <QVector> 0011 0012 #include <cstdio> 0013 0014 class QString; 0015 0016 /** 0017 * @short A structure describing a data field in the binary file. Its size is 16 bytes. 0018 */ 0019 typedef struct dataElement 0020 { 0021 char name[10] = ""; /**< Field name (eg. RA) */ 0022 qint8 size { 0 }; /**< Field size in bytes (eg. 4) */ 0023 quint8 type { 0 }; 0024 qint32 scale { 0 }; /**< Field scale. The final field value = raw_value * scale */ 0025 } dataElement; 0026 0027 /** 0028 * @class BinFileHelper 0029 * 0030 * This class provides utility functions to handle binary data files in the format prescribed 0031 * by KStars. The file format is designed specifically to support data that has the form of an 0032 * array of structures. See data/README.fileformat for details. 0033 * The methods use primitive C file I/O routines defined in stdio.h to obtain efficiency 0034 * @short Implements an interface to handle binary data files used by KStars 0035 * @author Akarsh Simha 0036 * @version 1.0 0037 */ 0038 class BinFileHelper 0039 { 0040 public: 0041 /** Constructor */ 0042 BinFileHelper(); 0043 0044 /** Destructor */ 0045 ~BinFileHelper(); 0046 0047 /** 0048 * @short Checks if a file exists. WARNING: Might be incompatible in other locales 0049 */ 0050 static bool testFileExists(const QString &fileName); 0051 0052 /** 0053 * WARNING: This function may not be compatible in other locales, because it calls QString::toAscii 0054 * @short Open a Binary data file and set the handle 0055 * @param fileName Reference to QString containing the name of the file 0056 * @return Handle to the file if successful, nullptr if an error occurred, sets the error. 0057 */ 0058 0059 FILE *openFile(const QString &fileName); 0060 0061 /** 0062 * @short Read the header and index table from the file and fill up the QVector s with the entries 0063 * @return True if successful, false if an error occurred, sets the error. 0064 */ 0065 bool readHeader(); 0066 0067 /** 0068 * @short Close the binary data file 0069 */ 0070 void closeFile(); 0071 0072 /** 0073 * @short Get error number 0074 * @return A number corresponding to the error 0075 */ 0076 int getErrorNumber(); 0077 0078 /** 0079 * @short Get error string 0080 * @return A QString containing the error message to be displayed 0081 */ 0082 QString getError(); 0083 0084 /** 0085 * @short Get field by name 0086 * @param fieldName Name of the field 0087 * @return A dataElement structure containing field data if the field exists, 0088 * containing junk values if the field doesn't exist 0089 */ 0090 struct dataElement getField(const QString &fieldName) const; 0091 0092 /** 0093 * @short Check whether a field exists 0094 * @param FieldName Name of the field 0095 * @return True if the field exists, false if it does not or the FD hasn't been read yet 0096 */ 0097 bool isField(const QString &FieldName) const; 0098 0099 /** 0100 * @short Check whether file properties are real or bogus 0101 * @return The value of private variable indexUpdated, which is true if data has been updated 0102 */ 0103 inline bool propertiesUpdated() const { return indexUpdated; } 0104 0105 /** 0106 * @short Get the file handle corresponding to the currently open file 0107 * @return The filehandle if a file is open, nullptr if no file is open 0108 */ 0109 inline FILE *getFileHandle() const { return fileHandle; } 0110 0111 /** 0112 * @short Returns the offset in the file corresponding to the given index ID 0113 * @param id ID of the index entry whose offset is required 0114 * @return The offset corresponding to that index ID, 0 if the index hasn't been read 0115 */ 0116 inline long getOffset(int id) const { return (indexUpdated ? indexOffset.at(id) : 0); } 0117 0118 /** 0119 * @short Returns the number of records under the given index ID 0120 * @param id ID of the index entry 0121 * @return The number of records under index that index ID, or 0 if the index has not been read 0122 */ 0123 inline unsigned int getRecordCount(int id) const { return (indexUpdated ? indexCount.at(id) : 0); } 0124 0125 /** 0126 * @short Returns the total number of records in the file 0127 * @return The number of records in the file, or 0 if the index has not been read 0128 */ 0129 inline unsigned long getRecordCount() const { return (indexUpdated ? recordCount : 0); } 0130 0131 /** 0132 * @short Should we do byte swapping? 0133 * @note To be called only after the header has been parsed 0134 * @return true if we must do byte swapping, false if not 0135 */ 0136 inline bool getByteSwap() const { return byteswap; } 0137 0138 /** 0139 * @short Return a guessed record size 0140 * @note The record size returned is guessed from the field descriptor table and may 0141 * not be correct in many cases 0142 * @return A guessed size of the record, or zero if the FD hasn't been read yet 0143 */ 0144 inline int guessRecordSize() const { return (RSUpdated ? recordSize : 0); } 0145 0146 /** 0147 * @short Override record size 0148 * @note To be used to override the guess in case the guess is wrong. This method should be called 0149 * before calling readHeader(). Use this only if you are sure of the recordSize or are sure 0150 * that the guess will not work. 0151 * @param rs The correct recordSize 0152 */ 0153 inline void setRecordSize(int rs) 0154 { 0155 recordSize = rs; 0156 RSUpdated = true; 0157 } 0158 0159 /** 0160 * @short Returns the number of fields as suggested by the field descriptor in the header 0161 * @return Number of fields, or zero if the FD hasn't been read yet 0162 */ 0163 inline int getFieldCount() const { return (FDUpdated ? nfields : 0); } 0164 0165 /** 0166 * @short Returns the header text 0167 * @return QString containing the header text if header has been read, blank QString otherwise 0168 */ 0169 inline QString getHeaderText() const { return (preambleUpdated ? headerText : ""); } 0170 0171 /** 0172 * @short Returns the offset at which the data begins 0173 * @return The value of dataOffset if indexUpdated, else zero 0174 */ 0175 inline long getDataOffset() const { return (indexUpdated ? dataOffset : 0); } 0176 0177 /** 0178 * @short Returns the offset at which the index table begins 0179 * @return The value of itableOffset if FDUpdated, else zero 0180 */ 0181 inline long getIndexTableOffset() const { return (FDUpdated ? itableOffset : 0); } 0182 0183 /** 0184 * @short Wrapper around fseek for large offsets 0185 * 0186 * fseek takes a signed long for the offset, since it can be 0187 * either positive or negative. If the argument is a 32-bit 0188 * unsigned integer, fseek will fail in most situations, since 0189 * that will be interpreted as a number of the opposite sign. 0190 * This wrapper circumvents that problem by repeatedly calling 0191 * fseek twice if the offset is too large. Useful when 64-bit 0192 * handling is really not necessary. 0193 * 0194 * @return Zero on success. Else, error code of the first failing fseek call. 0195 * @note Clearly, this method can move forward only. When we need 0196 * one that goes back, we'll implement that. 0197 */ 0198 static int unsigned_KDE_fseek(FILE *stream, quint32 offset, int whence); 0199 0200 /*! @short An enum providing user-friendly names for errors encountered */ 0201 enum Errors 0202 { 0203 ERR_NULL, /*!< No error occurred */ 0204 ERR_FILEOPEN, /*!< File could not be opened */ 0205 ERR_FD_TRUNC, /*!< Field descriptor table is truncated */ 0206 ERR_INDEX_TRUNC, /*!< File ends prematurely, before expected end of index table */ 0207 ERR_INDEX_BADID, /*!< Index table has an invalid ID entry */ 0208 ERR_INDEX_IDMISMATCH, /*!< Index table has a mismatched ID entry [ID found in the wrong place] */ 0209 ERR_INDEX_BADOFFSET, /*!< Offset / Record count specified in the Index table is bad */ 0210 ERR_BADSEEK /*!< Premature end of file / bad seek while reading index table */ 0211 }; 0212 0213 private: 0214 /** 0215 * @short Backends for readHeader without cleanup on abort 0216 * @return Return the appropriate error code, or ERR_NULL if successful 0217 */ 0218 enum Errors __readHeader(); 0219 0220 /** 0221 * @short Helper function that clears all field entries in the QVector fields 0222 */ 0223 void clearFields(); 0224 0225 /** 0226 * @short Helper function that sets all variables to their initial values 0227 */ 0228 void init(); 0229 0230 /// Handle to the file. 0231 FILE *fileHandle { nullptr}; 0232 /// Stores offsets corresponding to each index table entry 0233 QVector<unsigned long> indexOffset; 0234 /// Stores number of records under each index table entry 0235 QVector<unsigned int> indexCount; 0236 /// True if the data from the index, and associated properties have been updated 0237 bool indexUpdated { false }; 0238 /// True if the data from the Field Descriptor, and associated properties have been updated 0239 bool FDUpdated { false }; 0240 /// True if the recordSize parameter is set correctly, either manually or bye reading the FD 0241 bool RSUpdated { false }; 0242 /// True if the data from the preamble and associated properties have been updated 0243 bool preambleUpdated { false }; 0244 /// Stores the number corresponding to the previous error 0245 enum Errors errnum { ERR_NULL }; 0246 /// True if byteswapping should be done 0247 bool byteswap { false }; 0248 /// Stores the size of a record in bytes for quick retrieval 0249 int recordSize { 0 }; 0250 /// Maintains a list of fields in the file, along with relevant details 0251 QVector<dataElement *> fields; 0252 /// Stores the file header text 0253 QString headerText; 0254 /// Stores the number of fields as indicated by the field descriptor 0255 qint16 nfields { 0 }; 0256 /// Stores the size of the index table in number of entries 0257 quint32 indexSize { 0 }; 0258 /// Stores the offset position of the first index table entry 0259 long itableOffset { 0 }; 0260 /// Stores the offset position of the start of data 0261 long dataOffset { 0 }; 0262 /// Stores the most recent 'unread' error message 0263 QString errorMessage; 0264 /// Stores the total number of records in the file 0265 unsigned long recordCount { 0 }; 0266 /// Stores the version number of the file 0267 quint8 versionNumber { 0 }; 0268 };