Kstars

binfilehelper.cpp
1 /*
2  SPDX-FileCopyrightText: 2008 Akarsh Simha <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "binfilehelper.h"
8 
9 #include "byteorder.h"
10 #include "auxiliary/kspaths.h"
11 
12 #include <QStandardPaths>
13 
14 class BinFileHelper;
15 
17 {
18  init();
19 }
20 
22 {
23  qDeleteAll(fields);
24  fields.clear();
25  if (fileHandle)
26  closeFile();
27 }
28 
29 void BinFileHelper::init()
30 {
31  if (fileHandle)
32  fclose(fileHandle);
33 
34  fileHandle = nullptr;
35  indexUpdated = false;
36  FDUpdated = false;
37  RSUpdated = false;
38  preambleUpdated = false;
39  byteswap = false;
40  errnum = ERR_NULL;
41  recordCount = 0;
42  recordSize = 0;
43  nfields = 0;
44  indexSize = 0;
45  itableOffset = 0;
46  dataOffset = 0;
47  recordCount = 0;
48  versionNumber = 0;
49 }
50 
51 void BinFileHelper::clearFields()
52 {
53  qDeleteAll(fields);
54  fields.clear();
55 }
56 
58 {
59  QString FilePath = KSPaths::locate(QStandardPaths::AppLocalDataLocation, fileName);
60  QByteArray b = FilePath.toLatin1();
61  const char *filepath = b.data();
62  FILE *f = fopen(filepath, "rb");
63  if (f)
64  {
65  fclose(f);
66  return true;
67  }
68  else
69  return false;
70 }
71 
72 FILE *BinFileHelper::openFile(const QString &fileName)
73 {
74  QString FilePath = KSPaths::locate(QStandardPaths::AppLocalDataLocation, fileName);
75  init();
76  QByteArray b = FilePath.toLatin1();
77  const char *filepath = b.data();
78 
79  fileHandle = fopen(filepath, "rb");
80 
81  if (!fileHandle)
82  {
83  errnum = ERR_FILEOPEN;
84  return nullptr;
85  }
86  return fileHandle;
87 }
88 
89 enum BinFileHelper::Errors BinFileHelper::__readHeader()
90 {
91  qint16 endian_id, i;
92  char ASCII_text[125];
93  int ret = 0;
94 
95  // Read the preamble
96  if (!fileHandle)
97  return ERR_FILEOPEN;
98 
99  rewind(fileHandle);
100 
101  // Read the first 124 bytes of the binary file which contains a general text about the binary data.
102  // e.g. "KStars Star Data v1.0. To be read using the 32-bit StarData structure only"
103  ret = fread(ASCII_text, 124, 1, fileHandle); // cppcheck-suppress redundantAssignment
104  ASCII_text[124] = '\0';
105  headerText = ASCII_text;
106 
107  // Find out endianess from reading "KS" 0x4B53 in the binary file which was encoded on a little endian machine
108  // Therefore, in the binary file it is written as 53 4B (little endian as least significant byte is stored first),
109  // and when read on a little endian machine then it results in 0x4B53 (least significant byte is stored first in memory),
110  // whereas a big endian machine would read it as 0x534B (most significant byte is stored first in memory).
111  ret = fread(&endian_id, 2, 1, fileHandle); // cppcheck-suppress redundantAssignment
112  if (endian_id != 0x4B53)
113  byteswap = 1;
114  else
115  byteswap = 0;
116 
117  ret = fread(&versionNumber, 1, 1, fileHandle); // cppcheck-suppress redundantAssignment
118 
119  preambleUpdated = true;
120  // Read the field descriptor table
121  ret = fread(&nfields, 2, 1, fileHandle); // cppcheck-suppress redundantAssignment
122  if (byteswap)
123  nfields = bswap_16(nfields);
124 
125  qDeleteAll(fields);
126  fields.clear();
127  for (i = 0; i < nfields; ++i)
128  {
129  dataElement *de = new dataElement;
130 
131  // Read 16 byte dataElement that describe each field (name[8], size[1], type[1], scale[4])
132  if (!fread(de, sizeof(dataElement), 1, fileHandle))
133  {
134  delete de;
135  qDeleteAll(fields);
136  fields.clear();
137  return ERR_FD_TRUNC;
138  }
139  if (byteswap)
140  de->scale = bswap_32(de->scale);
141  fields.append(de);
142  }
143 
144  if (!RSUpdated)
145  {
146  recordSize = 0;
147  for (i = 0; i < fields.size(); ++i)
148  recordSize += fields[i]->size;
149  RSUpdated = true;
150  }
151 
152  FDUpdated = true;
153 
154  // Read the index table
155  ret = fread(&indexSize, 4, 1, fileHandle); // cppcheck-suppress redundantAssignment
156  if (byteswap)
157  indexSize = bswap_32(indexSize);
158 
159  quint32 j;
160  quint32 ID;
161  quint32 offset;
162  quint32 prev_offset;
163  quint32 nrecs;
164  quint32 prev_nrecs;
165 
166  // Find out current offset so far in the binary file (how many bytes we read so far)
167  itableOffset = ftell(fileHandle);
168 
169  prev_offset = 0;
170  prev_nrecs = 0;
171  recordCount = 0;
172 
173  indexCount.clear();
174  indexOffset.clear();
175 
176  if (indexSize == 0)
177  {
178  errorMessage = QStringLiteral("Zero index size!");
179  return ERR_INDEX_TRUNC;
180  }
181 
182  // We read each 12-byte index entry (ID[4], Offset[4] within file in bytes, nrec[4] # of Records).
183  // After reading all the indexes, we are (itableOffset + indexSize * 12) bytes within the file
184  // indexSize is usually the size of the HTM level (eg. HTM level 3 --> 512)
185  for (j = 0; j < indexSize; ++j)
186  {
187  if (!fread(&ID, 4, 1, fileHandle))
188  {
189  errorMessage = QStringLiteral("Table truncated before expected! Read i = %1 index entries so far").arg(QString::number(j));
190  return ERR_INDEX_TRUNC;
191  }
192 
193  if (byteswap)
194  ID = bswap_32(ID);
195 
196  if (ID >= indexSize)
197  {
198  errorMessage = QString::asprintf("ID %u is greater than the expected number of expected entries (%u)", ID, indexSize);
199  return ERR_INDEX_BADID;
200  }
201 
202  if (ID != j)
203  {
204  errorMessage = QString::asprintf("Found ID %u, at the location where ID %u was expected", ID, j);
205  return ERR_INDEX_IDMISMATCH;
206  }
207 
208  if (!fread(&offset, 4, 1, fileHandle))
209  {
210  errorMessage = QString::asprintf("Table truncated before expected! Read i = %d index entries so far", j);
211  return ERR_BADSEEK;
212  }
213 
214  if (byteswap)
215  offset = bswap_32(offset);
216 
217  if (!fread(&nrecs, 4, 1, fileHandle))
218  {
219  errorMessage = QString::asprintf("Table truncated before expected! Read i = %d index entries so far", j);
220  return ERR_BADSEEK;
221  }
222 
223  if (byteswap)
224  nrecs = bswap_32(nrecs);
225 
226  //if (prev_offset != 0 && prev_nrecs != (-prev_offset + offset) / recordSize)
227  if (prev_offset != 0 && prev_nrecs != (offset - prev_offset) / recordSize)
228  {
229  errorMessage = QString::asprintf("Expected %u = (%X - %x) / %x records, but found %u, in index entry %u",
230  (offset - prev_offset) / recordSize, offset, prev_offset, recordSize, prev_nrecs,
231  j - 1);
232  return ERR_INDEX_BADOFFSET;
233  }
234 
235  indexOffset.append(offset);
236  indexCount.append(nrecs);
237 
238  recordCount += nrecs;
239  prev_offset = offset;
240  prev_nrecs = nrecs;
241  }
242 
243  dataOffset = ftell(fileHandle);
244 
245  indexUpdated = true;
246 
247  return ERR_NULL;
248 }
249 
251 {
252  switch ((errnum = __readHeader()))
253  {
254  case ERR_NULL:
255  return true;
256  break;
257  case ERR_FILEOPEN:
258  return false;
259  break;
260  case ERR_FD_TRUNC:
261  clearFields();
262  break;
263  case ERR_INDEX_TRUNC:
264  case ERR_INDEX_BADID:
266  case ERR_BADSEEK:
267  case ERR_INDEX_BADOFFSET:
268  {
269  indexOffset.clear();
270  indexCount.clear();
271  }
272  }
273  return false;
274 }
275 
277 {
278  fclose(fileHandle);
279  fileHandle = nullptr;
280 }
281 
283 {
284  int err = errnum;
285  errnum = ERR_NULL;
286  return err;
287 }
288 
290 {
291  QString erm = errorMessage;
292  errorMessage = "";
293  return erm;
294 }
295 
296 struct dataElement BinFileHelper::getField(const QString &fieldName) const
297 {
298  dataElement de;
299 
300  for (auto &field : fields)
301  {
302  if (field->name == fieldName)
303  {
304  de = *field;
305  return de;
306  }
307  }
308  return de; // Returns junk!
309 }
310 
311 bool BinFileHelper::isField(const QString &fieldName) const
312 {
313  for (auto &field : fields)
314  {
315  if (field->name == fieldName)
316  return true;
317  }
318  return false;
319 }
320 
321 int BinFileHelper::unsigned_KDE_fseek(FILE *stream, quint32 offset, int whence)
322 {
323  Q_ASSERT(stream);
324  int ret = 0;
325  if (offset <= ((quint32)1 << 31) - 1)
326  {
327  ret = fseek(stream, offset, whence);
328  }
329  else
330  {
331  // Do the fseek in two steps
332  ret = fseek(stream, ((quint32)1 << 31) - 1, whence);
333  if (!ret)
334  ret = fseek(stream, offset - ((quint32)1 << 31) + 1, SEEK_CUR);
335  }
336  return ret;
337 }
static int unsigned_KDE_fseek(FILE *stream, quint32 offset, int whence)
Wrapper around fseek for large offsets.
QString number(int n, int base)
static bool testFileExists(const QString &fileName)
Checks if a file exists.
bool readHeader()
Read the header and index table from the file and fill up the QVector s with the entries.
void append(const T &value)
QByteArray toLatin1() const const
~BinFileHelper()
Destructor.
bool isField(const QString &FieldName) const
Check whether a field exists.
void closeFile()
Close the binary data file.
void clear()
QString getError()
Get error string.
Errors
An enum providing user-friendly names for errors encountered.
Implements an interface to handle binary data files used by KStars.
Definition: binfilehelper.h:38
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
int size() const const
A structure describing a data field in the binary file.
Definition: binfilehelper.h:19
QString asprintf(const char *cformat,...)
BinFileHelper()
Constructor.
qint32 scale
Field scale.
Definition: binfilehelper.h:24
char * data()
FILE * openFile(const QString &fileName)
WARNING: This function may not be compatible in other locales, because it calls QString::toAscii.
int getErrorNumber()
Get error number.
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Jun 4 2023 03:57:11 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.