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 #include "binfilehelper.h" 0008 0009 #include "byteorder.h" 0010 #include "auxiliary/kspaths.h" 0011 0012 #include <QStandardPaths> 0013 0014 class BinFileHelper; 0015 0016 BinFileHelper::BinFileHelper() 0017 { 0018 init(); 0019 } 0020 0021 BinFileHelper::~BinFileHelper() 0022 { 0023 qDeleteAll(fields); 0024 fields.clear(); 0025 if (fileHandle) 0026 closeFile(); 0027 } 0028 0029 void BinFileHelper::init() 0030 { 0031 if (fileHandle) 0032 fclose(fileHandle); 0033 0034 fileHandle = nullptr; 0035 indexUpdated = false; 0036 FDUpdated = false; 0037 RSUpdated = false; 0038 preambleUpdated = false; 0039 byteswap = false; 0040 errnum = ERR_NULL; 0041 recordCount = 0; 0042 recordSize = 0; 0043 nfields = 0; 0044 indexSize = 0; 0045 itableOffset = 0; 0046 dataOffset = 0; 0047 recordCount = 0; 0048 versionNumber = 0; 0049 } 0050 0051 void BinFileHelper::clearFields() 0052 { 0053 qDeleteAll(fields); 0054 fields.clear(); 0055 } 0056 0057 bool BinFileHelper::testFileExists(const QString &fileName) 0058 { 0059 QString FilePath = KSPaths::locate(QStandardPaths::AppLocalDataLocation, fileName); 0060 QByteArray b = FilePath.toLatin1(); 0061 const char *filepath = b.data(); 0062 FILE *f = fopen(filepath, "rb"); 0063 if (f) 0064 { 0065 fclose(f); 0066 return true; 0067 } 0068 else 0069 return false; 0070 } 0071 0072 FILE *BinFileHelper::openFile(const QString &fileName) 0073 { 0074 QString FilePath = KSPaths::locate(QStandardPaths::AppLocalDataLocation, fileName); 0075 init(); 0076 QByteArray b = FilePath.toLatin1(); 0077 const char *filepath = b.data(); 0078 0079 fileHandle = fopen(filepath, "rb"); 0080 0081 if (!fileHandle) 0082 { 0083 errnum = ERR_FILEOPEN; 0084 return nullptr; 0085 } 0086 return fileHandle; 0087 } 0088 0089 enum BinFileHelper::Errors BinFileHelper::__readHeader() 0090 { 0091 qint16 endian_id, i; 0092 char ASCII_text[125]; 0093 int ret = 0; 0094 0095 // Read the preamble 0096 if (!fileHandle) 0097 return ERR_FILEOPEN; 0098 0099 rewind(fileHandle); 0100 0101 // Read the first 124 bytes of the binary file which contains a general text about the binary data. 0102 // e.g. "KStars Star Data v1.0. To be read using the 32-bit StarData structure only" 0103 ret = fread(ASCII_text, 124, 1, fileHandle); // cppcheck-suppress redundantAssignment 0104 ASCII_text[124] = '\0'; 0105 headerText = ASCII_text; 0106 0107 // Find out endianess from reading "KS" 0x4B53 in the binary file which was encoded on a little endian machine 0108 // Therefore, in the binary file it is written as 53 4B (little endian as least significant byte is stored first), 0109 // and when read on a little endian machine then it results in 0x4B53 (least significant byte is stored first in memory), 0110 // whereas a big endian machine would read it as 0x534B (most significant byte is stored first in memory). 0111 ret = fread(&endian_id, 2, 1, fileHandle); // cppcheck-suppress redundantAssignment 0112 if (endian_id != 0x4B53) 0113 byteswap = 1; 0114 else 0115 byteswap = 0; 0116 0117 ret = fread(&versionNumber, 1, 1, fileHandle); // cppcheck-suppress redundantAssignment 0118 0119 preambleUpdated = true; 0120 // Read the field descriptor table 0121 ret = fread(&nfields, 2, 1, fileHandle); // cppcheck-suppress redundantAssignment 0122 if (byteswap) 0123 nfields = bswap_16(nfields); 0124 0125 qDeleteAll(fields); 0126 fields.clear(); 0127 for (i = 0; i < nfields; ++i) 0128 { 0129 dataElement *de = new dataElement; 0130 0131 // Read 16 byte dataElement that describe each field (name[8], size[1], type[1], scale[4]) 0132 if (!fread(de, sizeof(dataElement), 1, fileHandle)) 0133 { 0134 delete de; 0135 qDeleteAll(fields); 0136 fields.clear(); 0137 return ERR_FD_TRUNC; 0138 } 0139 if (byteswap) 0140 de->scale = bswap_32(de->scale); 0141 fields.append(de); 0142 } 0143 0144 if (!RSUpdated) 0145 { 0146 recordSize = 0; 0147 for (i = 0; i < fields.size(); ++i) 0148 recordSize += fields[i]->size; 0149 RSUpdated = true; 0150 } 0151 0152 FDUpdated = true; 0153 0154 // Read the index table 0155 ret = fread(&indexSize, 4, 1, fileHandle); // cppcheck-suppress redundantAssignment 0156 if (byteswap) 0157 indexSize = bswap_32(indexSize); 0158 0159 quint32 j; 0160 quint32 ID; 0161 quint32 offset; 0162 quint32 prev_offset; 0163 quint32 nrecs; 0164 quint32 prev_nrecs; 0165 0166 // Find out current offset so far in the binary file (how many bytes we read so far) 0167 itableOffset = ftell(fileHandle); 0168 0169 prev_offset = 0; 0170 prev_nrecs = 0; 0171 recordCount = 0; 0172 0173 indexCount.clear(); 0174 indexOffset.clear(); 0175 0176 if (indexSize == 0) 0177 { 0178 errorMessage = QStringLiteral("Zero index size!"); 0179 return ERR_INDEX_TRUNC; 0180 } 0181 0182 // We read each 12-byte index entry (ID[4], Offset[4] within file in bytes, nrec[4] # of Records). 0183 // After reading all the indexes, we are (itableOffset + indexSize * 12) bytes within the file 0184 // indexSize is usually the size of the HTM level (eg. HTM level 3 --> 512) 0185 for (j = 0; j < indexSize; ++j) 0186 { 0187 if (!fread(&ID, 4, 1, fileHandle)) 0188 { 0189 errorMessage = QStringLiteral("Table truncated before expected! Read i = %1 index entries so far").arg(QString::number(j)); 0190 return ERR_INDEX_TRUNC; 0191 } 0192 0193 if (byteswap) 0194 ID = bswap_32(ID); 0195 0196 if (ID >= indexSize) 0197 { 0198 errorMessage = QString::asprintf("ID %u is greater than the expected number of expected entries (%u)", ID, indexSize); 0199 return ERR_INDEX_BADID; 0200 } 0201 0202 if (ID != j) 0203 { 0204 errorMessage = QString::asprintf("Found ID %u, at the location where ID %u was expected", ID, j); 0205 return ERR_INDEX_IDMISMATCH; 0206 } 0207 0208 if (!fread(&offset, 4, 1, fileHandle)) 0209 { 0210 errorMessage = QString::asprintf("Table truncated before expected! Read i = %d index entries so far", j); 0211 return ERR_BADSEEK; 0212 } 0213 0214 if (byteswap) 0215 offset = bswap_32(offset); 0216 0217 if (!fread(&nrecs, 4, 1, fileHandle)) 0218 { 0219 errorMessage = QString::asprintf("Table truncated before expected! Read i = %d index entries so far", j); 0220 return ERR_BADSEEK; 0221 } 0222 0223 if (byteswap) 0224 nrecs = bswap_32(nrecs); 0225 0226 //if (prev_offset != 0 && prev_nrecs != (-prev_offset + offset) / recordSize) 0227 if (prev_offset != 0 && prev_nrecs != (offset - prev_offset) / recordSize) 0228 { 0229 errorMessage = QString::asprintf("Expected %u = (%X - %x) / %x records, but found %u, in index entry %u", 0230 (offset - prev_offset) / recordSize, offset, prev_offset, recordSize, prev_nrecs, 0231 j - 1); 0232 return ERR_INDEX_BADOFFSET; 0233 } 0234 0235 indexOffset.append(offset); 0236 indexCount.append(nrecs); 0237 0238 recordCount += nrecs; 0239 prev_offset = offset; 0240 prev_nrecs = nrecs; 0241 } 0242 0243 dataOffset = ftell(fileHandle); 0244 0245 indexUpdated = true; 0246 0247 return ERR_NULL; 0248 } 0249 0250 bool BinFileHelper::readHeader() 0251 { 0252 switch ((errnum = __readHeader())) 0253 { 0254 case ERR_NULL: 0255 return true; 0256 break; 0257 case ERR_FILEOPEN: 0258 return false; 0259 break; 0260 case ERR_FD_TRUNC: 0261 clearFields(); 0262 break; 0263 case ERR_INDEX_TRUNC: 0264 case ERR_INDEX_BADID: 0265 case ERR_INDEX_IDMISMATCH: 0266 case ERR_BADSEEK: 0267 case ERR_INDEX_BADOFFSET: 0268 { 0269 indexOffset.clear(); 0270 indexCount.clear(); 0271 } 0272 } 0273 return false; 0274 } 0275 0276 void BinFileHelper::closeFile() 0277 { 0278 fclose(fileHandle); 0279 fileHandle = nullptr; 0280 } 0281 0282 int BinFileHelper::getErrorNumber() 0283 { 0284 int err = errnum; 0285 errnum = ERR_NULL; 0286 return err; 0287 } 0288 0289 QString BinFileHelper::getError() 0290 { 0291 QString erm = errorMessage; 0292 errorMessage = ""; 0293 return erm; 0294 } 0295 0296 struct dataElement BinFileHelper::getField(const QString &fieldName) const 0297 { 0298 dataElement de; 0299 0300 for (auto &field : fields) 0301 { 0302 if (field->name == fieldName) 0303 { 0304 de = *field; 0305 return de; 0306 } 0307 } 0308 return de; // Returns junk! 0309 } 0310 0311 bool BinFileHelper::isField(const QString &fieldName) const 0312 { 0313 for (auto &field : fields) 0314 { 0315 if (field->name == fieldName) 0316 return true; 0317 } 0318 return false; 0319 } 0320 0321 int BinFileHelper::unsigned_KDE_fseek(FILE *stream, quint32 offset, int whence) 0322 { 0323 Q_ASSERT(stream); 0324 int ret = 0; 0325 if (offset <= ((quint32)1 << 31) - 1) 0326 { 0327 ret = fseek(stream, offset, whence); 0328 } 0329 else 0330 { 0331 // Do the fseek in two steps 0332 ret = fseek(stream, ((quint32)1 << 31) - 1, whence); 0333 if (!ret) 0334 ret = fseek(stream, offset - ((quint32)1 << 31) + 1, SEEK_CUR); 0335 } 0336 return ret; 0337 }