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 }